Coverage Report

Created: 2024-05-16 12:16

/__w/smoldot/smoldot/repo/lib/src/executor/vm/jit.rs
Line
Count
Source (jump to first uncovered line)
1
// Smoldot
2
// Copyright (C) 2019-2022  Parity Technologies (UK) Ltd.
3
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
4
5
// This program is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
9
10
// This program is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
15
// You should have received a copy of the GNU General Public License
16
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18
//! Implements the API documented [in the parent module](..).
19
20
use super::{
21
    ExecOutcome, GlobalValueErr, HeapPages, NewErr, OutOfBoundsError, RunErr, Signature, StartErr,
22
    Trap, ValueType, WasmValue,
23
};
24
25
use alloc::{boxed::Box, sync::Arc, vec::Vec};
26
use core::{fmt, future, mem, pin, ptr, slice, task};
27
// TODO: we use std::sync::Mutex rather than parking_lot::Mutex due to issues with Cargo features, see <https://github.com/paritytech/smoldot/issues/2732>
28
use std::sync::Mutex;
29
30
/// See [`super::VirtualMachinePrototype`].
31
pub struct JitPrototype {
32
    /// Base components that can be used to recreate a prototype later if desired.
33
    base_components: BaseComponents,
34
35
    store: wasmtime::Store<()>,
36
37
    /// Instantiated Wasm VM.
38
    instance: wasmtime::Instance,
39
40
    /// Shared between the "outside" and the external functions. See [`Shared`].
41
    shared: Arc<Mutex<Shared>>,
42
43
    /// Reference to the memory used by the module.
44
    memory: wasmtime::Memory,
45
46
    /// The type associated with [`JitPrototype`].
47
    memory_type: wasmtime::MemoryType,
48
}
49
50
struct BaseComponents {
51
    module: wasmtime::Module,
52
53
    /// For each import of the module, either `None` if not a function, or `Some` containing the
54
    /// `usize` of that function.
55
    resolved_imports: Vec<Option<usize>>,
56
}
57
58
impl JitPrototype {
59
    /// See [`super::VirtualMachinePrototype::new`].
60
92
    pub fn new(
61
92
        module_bytes: &[u8],
62
92
        symbols: &mut dyn FnMut(&str, &str, &Signature) -> Result<usize, ()>,
63
92
    ) -> Result<Self, NewErr> {
64
92
        let mut config = wasmtime::Config::new();
65
92
        config.cranelift_nan_canonicalization(true);
66
92
        config.cranelift_opt_level(wasmtime::OptLevel::Speed);
67
92
        config.async_support(true);
68
92
        // The default value of `wasm_backtrace_details` is `Environment`, which reads the
69
92
        // `WASMTIME_BACKTRACE_DETAILS` environment variable to determine whether or not to keep
70
92
        // debug info. However we don't want any of the behaviour of our code to rely on any
71
92
        // environment variables whatsoever. Whether to use `Enable` or `Disable` below isn't
72
92
        // very important, so long as it is not `Environment`.
73
92
        config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable);
74
92
75
92
        // Disable all post-MVP wasm features.
76
92
        // Some of these configuration options are `true` by default while some others are `false`
77
92
        // by default, but we just disable them all to be sure.
78
92
        config.wasm_threads(false);
79
92
        config.wasm_reference_types(false);
80
92
        config.wasm_function_references(false);
81
92
        config.wasm_simd(false);
82
92
        config.wasm_relaxed_simd(false);
83
92
        config.wasm_bulk_memory(false);
84
92
        config.wasm_multi_value(false);
85
92
        config.wasm_multi_memory(false);
86
92
        config.wasm_memory64(false);
87
92
        config.wasm_tail_call(false);
88
92
        config.wasm_component_model(false);
89
90
92
        let engine =
91
92
            wasmtime::Engine::new(&config).map_err(|err| 
NewErr::InvalidWasm(err.to_string())0
)
?0
;
Unexecuted instantiation: _RNCNvMNtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB4_12JitPrototype3new0Ba_
Unexecuted instantiation: _RNCNvMNtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB4_12JitPrototype3new0Ba_
92
93
92
        let 
module82
= wasmtime::Module::from_binary(&engine, module_bytes)
94
92
            .map_err(|err| 
NewErr::InvalidWasm(err.to_string())10
)
?10
;
_RNCNvMNtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB4_12JitPrototype3news_0Ba_
Line
Count
Source
94
10
            .map_err(|err| NewErr::InvalidWasm(err.to_string()))?;
Unexecuted instantiation: _RNCNvMNtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB4_12JitPrototype3news_0Ba_
95
96
        // Building the list of imports that the Wasm VM is able to use.
97
76
        let resolved_imports = {
98
82
            let mut imports = Vec::with_capacity(module.imports().len());
99
891
            for import in 
module.imports()82
{
100
891
                match import.ty() {
101
833
                    wasmtime::ExternType::Func(func_type) => {
102
828
                        // Note that if `Signature::try_from` fails, a `UnresolvedFunctionImport` is
103
828
                        // also returned. This is because it is not possible for the function to
104
828
                        // resolve anyway if its signature can't be represented.
105
828
                        let function_index =
106
833
                            match Signature::try_from(&func_type)
107
833
                                .ok()
108
833
                                .and_then(|conv_signature| {
109
832
                                    symbols(import.module(), import.name(), &conv_signature).ok()
110
833
                                }) {
_RNCNvMNtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB4_12JitPrototype3news0_0Ba_
Line
Count
Source
108
165
                                .and_then(|conv_signature| {
109
165
                                    symbols(import.module(), import.name(), &conv_signature).ok()
110
165
                                }) {
_RNCNvMNtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB4_12JitPrototype3news0_0Ba_
Line
Count
Source
108
667
                                .and_then(|conv_signature| {
109
667
                                    symbols(import.module(), import.name(), &conv_signature).ok()
110
667
                                }) {
111
828
                                Some(i) => i,
112
                                None => {
113
5
                                    return Err(NewErr::UnresolvedFunctionImport {
114
5
                                        module_name: import.module().to_owned(),
115
5
                                        function: import.name().to_owned(),
116
5
                                    })
117
                                }
118
                            };
119
120
828
                        imports.push(Some(function_index));
121
                    }
122
                    wasmtime::ExternType::Global(_) | wasmtime::ExternType::Table(_) => {
123
1
                        return Err(NewErr::ImportTypeNotSupported);
124
                    }
125
57
                    wasmtime::ExternType::Memory(_) => {
126
57
                        imports.push(None);
127
57
                    }
128
                };
129
            }
130
76
            imports
131
76
        };
132
76
133
76
        Self::from_base_components(BaseComponents {
134
76
            module,
135
76
            resolved_imports,
136
76
        })
137
92
    }
_RNvMNtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB2_12JitPrototype3new
Line
Count
Source
60
69
    pub fn new(
61
69
        module_bytes: &[u8],
62
69
        symbols: &mut dyn FnMut(&str, &str, &Signature) -> Result<usize, ()>,
63
69
    ) -> Result<Self, NewErr> {
64
69
        let mut config = wasmtime::Config::new();
65
69
        config.cranelift_nan_canonicalization(true);
66
69
        config.cranelift_opt_level(wasmtime::OptLevel::Speed);
67
69
        config.async_support(true);
68
69
        // The default value of `wasm_backtrace_details` is `Environment`, which reads the
69
69
        // `WASMTIME_BACKTRACE_DETAILS` environment variable to determine whether or not to keep
70
69
        // debug info. However we don't want any of the behaviour of our code to rely on any
71
69
        // environment variables whatsoever. Whether to use `Enable` or `Disable` below isn't
72
69
        // very important, so long as it is not `Environment`.
73
69
        config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable);
74
69
75
69
        // Disable all post-MVP wasm features.
76
69
        // Some of these configuration options are `true` by default while some others are `false`
77
69
        // by default, but we just disable them all to be sure.
78
69
        config.wasm_threads(false);
79
69
        config.wasm_reference_types(false);
80
69
        config.wasm_function_references(false);
81
69
        config.wasm_simd(false);
82
69
        config.wasm_relaxed_simd(false);
83
69
        config.wasm_bulk_memory(false);
84
69
        config.wasm_multi_value(false);
85
69
        config.wasm_multi_memory(false);
86
69
        config.wasm_memory64(false);
87
69
        config.wasm_tail_call(false);
88
69
        config.wasm_component_model(false);
89
90
69
        let engine =
91
69
            wasmtime::Engine::new(&config).map_err(|err| NewErr::InvalidWasm(err.to_string()))
?0
;
92
93
69
        let 
module59
= wasmtime::Module::from_binary(&engine, module_bytes)
94
69
            .map_err(|err| NewErr::InvalidWasm(err.to_string()))
?10
;
95
96
        // Building the list of imports that the Wasm VM is able to use.
97
53
        let resolved_imports = {
98
59
            let mut imports = Vec::with_capacity(module.imports().len());
99
201
            for import in 
module.imports()59
{
100
201
                match import.ty() {
101
166
                    wasmtime::ExternType::Func(func_type) => {
102
161
                        // Note that if `Signature::try_from` fails, a `UnresolvedFunctionImport` is
103
161
                        // also returned. This is because it is not possible for the function to
104
161
                        // resolve anyway if its signature can't be represented.
105
161
                        let function_index =
106
166
                            match Signature::try_from(&func_type)
107
166
                                .ok()
108
166
                                .and_then(|conv_signature| {
109
                                    symbols(import.module(), import.name(), &conv_signature).ok()
110
166
                                }) {
111
161
                                Some(i) => i,
112
                                None => {
113
5
                                    return Err(NewErr::UnresolvedFunctionImport {
114
5
                                        module_name: import.module().to_owned(),
115
5
                                        function: import.name().to_owned(),
116
5
                                    })
117
                                }
118
                            };
119
120
161
                        imports.push(Some(function_index));
121
                    }
122
                    wasmtime::ExternType::Global(_) | wasmtime::ExternType::Table(_) => {
123
1
                        return Err(NewErr::ImportTypeNotSupported);
124
                    }
125
34
                    wasmtime::ExternType::Memory(_) => {
126
34
                        imports.push(None);
127
34
                    }
128
                };
129
            }
130
53
            imports
131
53
        };
132
53
133
53
        Self::from_base_components(BaseComponents {
134
53
            module,
135
53
            resolved_imports,
136
53
        })
137
69
    }
_RNvMNtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB2_12JitPrototype3new
Line
Count
Source
60
23
    pub fn new(
61
23
        module_bytes: &[u8],
62
23
        symbols: &mut dyn FnMut(&str, &str, &Signature) -> Result<usize, ()>,
63
23
    ) -> Result<Self, NewErr> {
64
23
        let mut config = wasmtime::Config::new();
65
23
        config.cranelift_nan_canonicalization(true);
66
23
        config.cranelift_opt_level(wasmtime::OptLevel::Speed);
67
23
        config.async_support(true);
68
23
        // The default value of `wasm_backtrace_details` is `Environment`, which reads the
69
23
        // `WASMTIME_BACKTRACE_DETAILS` environment variable to determine whether or not to keep
70
23
        // debug info. However we don't want any of the behaviour of our code to rely on any
71
23
        // environment variables whatsoever. Whether to use `Enable` or `Disable` below isn't
72
23
        // very important, so long as it is not `Environment`.
73
23
        config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable);
74
23
75
23
        // Disable all post-MVP wasm features.
76
23
        // Some of these configuration options are `true` by default while some others are `false`
77
23
        // by default, but we just disable them all to be sure.
78
23
        config.wasm_threads(false);
79
23
        config.wasm_reference_types(false);
80
23
        config.wasm_function_references(false);
81
23
        config.wasm_simd(false);
82
23
        config.wasm_relaxed_simd(false);
83
23
        config.wasm_bulk_memory(false);
84
23
        config.wasm_multi_value(false);
85
23
        config.wasm_multi_memory(false);
86
23
        config.wasm_memory64(false);
87
23
        config.wasm_tail_call(false);
88
23
        config.wasm_component_model(false);
89
90
23
        let engine =
91
23
            wasmtime::Engine::new(&config).map_err(|err| NewErr::InvalidWasm(err.to_string()))
?0
;
92
93
23
        let module = wasmtime::Module::from_binary(&engine, module_bytes)
94
23
            .map_err(|err| NewErr::InvalidWasm(err.to_string()))
?0
;
95
96
        // Building the list of imports that the Wasm VM is able to use.
97
23
        let resolved_imports = {
98
23
            let mut imports = Vec::with_capacity(module.imports().len());
99
690
            for import in 
module.imports()23
{
100
690
                match import.ty() {
101
667
                    wasmtime::ExternType::Func(func_type) => {
102
667
                        // Note that if `Signature::try_from` fails, a `UnresolvedFunctionImport` is
103
667
                        // also returned. This is because it is not possible for the function to
104
667
                        // resolve anyway if its signature can't be represented.
105
667
                        let function_index =
106
667
                            match Signature::try_from(&func_type)
107
667
                                .ok()
108
667
                                .and_then(|conv_signature| {
109
                                    symbols(import.module(), import.name(), &conv_signature).ok()
110
667
                                }) {
111
667
                                Some(i) => i,
112
                                None => {
113
0
                                    return Err(NewErr::UnresolvedFunctionImport {
114
0
                                        module_name: import.module().to_owned(),
115
0
                                        function: import.name().to_owned(),
116
0
                                    })
117
                                }
118
                            };
119
120
667
                        imports.push(Some(function_index));
121
                    }
122
                    wasmtime::ExternType::Global(_) | wasmtime::ExternType::Table(_) => {
123
0
                        return Err(NewErr::ImportTypeNotSupported);
124
                    }
125
23
                    wasmtime::ExternType::Memory(_) => {
126
23
                        imports.push(None);
127
23
                    }
128
                };
129
            }
130
23
            imports
131
23
        };
132
23
133
23
        Self::from_base_components(BaseComponents {
134
23
            module,
135
23
            resolved_imports,
136
23
        })
137
23
    }
138
139
85
    fn from_base_components(base_components: BaseComponents) -> Result<Self, NewErr> {
140
85
        let mut store = wasmtime::Store::new(base_components.module.engine(), ());
141
85
142
85
        let mut imported_memory = None;
143
85
        let shared = Arc::new(Mutex::new(Shared::ExecutingStart));
144
145
        // Building the list of symbols that the Wasm VM is able to use.
146
84
        let imports = {
147
85
            let mut imports = Vec::with_capacity(base_components.module.imports().len());
148
964
            for (module_import, resolved_function) in base_components
149
85
                .module
150
85
                .imports()
151
85
                .zip(base_components.resolved_imports.iter())
152
            {
153
964
                match module_import.ty() {
154
905
                    wasmtime::ExternType::Func(func_type) => {
155
905
                        let function_index = resolved_function.unwrap();
156
905
                        let shared = shared.clone();
157
905
158
905
                        // Obtain `expected_return_ty`. We know that the type is supported due to
159
905
                        // the signature check earlier.
160
905
                        let expected_return_ty = func_type
161
905
                            .results()
162
905
                            .next()
163
905
                            .map(|v| 
ValueType::try_from(v).unwrap()647
);
_RNCNvMNtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB4_12JitPrototype20from_base_components0Ba_
Line
Count
Source
163
143
                            .map(|v| ValueType::try_from(v).unwrap());
_RNCNvMNtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB4_12JitPrototype20from_base_components0Ba_
Line
Count
Source
163
504
                            .map(|v| ValueType::try_from(v).unwrap());
164
905
165
905
                        imports.push(wasmtime::Extern::Func(wasmtime::Func::new_async(
166
905
                            &mut store,
167
905
                            func_type,
168
4.19k
                            move |mut caller, params, ret_val| {
169
4.19k
                                // This closure is executed whenever the Wasm VM calls a
170
4.19k
                                // host function.
171
4.19k
                                // While a function call is in progress, only this closure can
172
4.19k
                                // have access to the `wasmtime::Store`. For this reason, we use
173
4.19k
                                // a small communication protocol with the outside.
174
4.19k
175
4.19k
                                // Transition `shared` from `OutsideFunctionCall` to
176
4.19k
                                // `EnteredFunctionCall`.
177
4.19k
                                {
178
4.19k
                                    let mut shared_lock = shared.try_lock().unwrap();
179
4.19k
                                    match mem::replace(&mut *shared_lock, Shared::Poisoned) {
180
4.19k
                                        Shared::OutsideFunctionCall { memory } => {
181
4.19k
                                            *shared_lock = Shared::EnteredFunctionCall {
182
4.19k
                                                function_index,
183
4.19k
                                                // Because the function signature has been
184
4.19k
                                                // validated at initialization, we can safely
185
4.19k
                                                // convert all the parameter types.
186
4.19k
                                                parameters: params
187
4.19k
                                                    .iter()
188
4.19k
                                                    .map(TryFrom::try_from)
189
4.19k
                                                    .collect::<Result<_, _>>()
190
4.19k
                                                    .unwrap(),
191
4.19k
                                                expected_return_ty,
192
4.19k
                                                in_interrupted_waker: None, // Filled below
193
4.19k
                                                memory: SliceRawParts(
194
4.19k
                                                    memory.data_ptr(&caller),
195
4.19k
                                                    memory.data_size(&caller),
196
4.19k
                                                ),
197
4.19k
                                            };
198
4.19k
                                        }
199
                                        Shared::ExecutingStart => {
200
1
                                            return Box::new(future::ready(Err(
201
1
                                                wasmtime::Error::new(
202
1
                                                    NewErr::StartFunctionNotSupported,
203
1
                                                ),
204
1
                                            )));
205
                                        }
206
0
                                        _ => unreachable!(),
207
                                    }
208
                                }
209
210
                                // Return a future that is ready whenever `Shared` contains
211
                                // `Return`.
212
4.19k
                                let shared = shared.clone();
213
8.39k
                                Box::new(future::poll_fn(move |cx| {
214
8.39k
                                    let mut shared_lock = shared.try_lock().unwrap();
215
8.39k
                                    match *shared_lock {
216
                                        Shared::EnteredFunctionCall {
217
4.19k
                                            ref mut in_interrupted_waker,
218
                                            ..
219
                                        }
220
                                        | Shared::WithinFunctionCall {
221
0
                                            ref mut in_interrupted_waker,
222
                                            ..
223
                                        } => {
224
4.19k
                                            *in_interrupted_waker = Some(cx.waker().clone());
225
4.19k
                                            task::Poll::Pending
226
                                        }
227
                                        Shared::MemoryGrowRequired {
228
6
                                            ref memory,
229
6
                                            additional,
230
6
                                        } => {
231
6
                                            // The outer call has made sure that `additional`
232
6
                                            // would fit.
233
6
                                            memory.grow(&mut caller, additional).unwrap();
234
6
                                            *shared_lock = Shared::WithinFunctionCall {
235
6
                                                in_interrupted_waker: Some(cx.waker().clone()),
236
6
                                                memory: SliceRawParts(
237
6
                                                    memory.data_ptr(&caller),
238
6
                                                    memory.data_size(&caller),
239
6
                                                ),
240
6
                                                expected_return_ty,
241
6
                                            };
242
6
                                            task::Poll::Pending
243
                                        }
244
                                        Shared::Return {
245
4.19k
                                            ref mut return_value,
246
4.19k
                                            memory,
247
                                        } => {
248
4.19k
                                            if let Some(
returned2.10k
) = return_value.take() {
249
2.10k
                                                assert_eq!(ret_val.len(), 1);
250
2.10k
                                                ret_val[0] = From::from(returned);
251
                                            } else {
252
2.08k
                                                assert!(ret_val.is_empty());
253
                                            }
254
255
4.19k
                                            *shared_lock = Shared::OutsideFunctionCall { memory };
256
4.19k
                                            task::Poll::Ready(Ok(()))
257
                                        }
258
0
                                        _ => unreachable!(),
259
                                    }
260
8.39k
                                }
))4.19k
_RNCNCNvMNtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB6_12JitPrototype20from_base_componentss_00Bc_
Line
Count
Source
213
85
                                Box::new(future::poll_fn(move |cx| {
214
85
                                    let mut shared_lock = shared.try_lock().unwrap();
215
85
                                    match *shared_lock {
216
                                        Shared::EnteredFunctionCall {
217
44
                                            ref mut in_interrupted_waker,
218
                                            ..
219
                                        }
220
                                        | Shared::WithinFunctionCall {
221
0
                                            ref mut in_interrupted_waker,
222
                                            ..
223
                                        } => {
224
44
                                            *in_interrupted_waker = Some(cx.waker().clone());
225
44
                                            task::Poll::Pending
226
                                        }
227
                                        Shared::MemoryGrowRequired {
228
0
                                            ref memory,
229
0
                                            additional,
230
0
                                        } => {
231
0
                                            // The outer call has made sure that `additional`
232
0
                                            // would fit.
233
0
                                            memory.grow(&mut caller, additional).unwrap();
234
0
                                            *shared_lock = Shared::WithinFunctionCall {
235
0
                                                in_interrupted_waker: Some(cx.waker().clone()),
236
0
                                                memory: SliceRawParts(
237
0
                                                    memory.data_ptr(&caller),
238
0
                                                    memory.data_size(&caller),
239
0
                                                ),
240
0
                                                expected_return_ty,
241
0
                                            };
242
0
                                            task::Poll::Pending
243
                                        }
244
                                        Shared::Return {
245
41
                                            ref mut return_value,
246
41
                                            memory,
247
                                        } => {
248
41
                                            if let Some(
returned27
) = return_value.take() {
249
27
                                                assert_eq!(ret_val.len(), 1);
250
27
                                                ret_val[0] = From::from(returned);
251
                                            } else {
252
14
                                                assert!(ret_val.is_empty());
253
                                            }
254
255
41
                                            *shared_lock = Shared::OutsideFunctionCall { memory };
256
41
                                            task::Poll::Ready(Ok(()))
257
                                        }
258
0
                                        _ => unreachable!(),
259
                                    }
260
85
                                }))
_RNCNCNvMNtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB6_12JitPrototype20from_base_componentss_00Bc_
Line
Count
Source
213
8.31k
                                Box::new(future::poll_fn(move |cx| {
214
8.31k
                                    let mut shared_lock = shared.try_lock().unwrap();
215
8.31k
                                    match *shared_lock {
216
                                        Shared::EnteredFunctionCall {
217
4.15k
                                            ref mut in_interrupted_waker,
218
                                            ..
219
                                        }
220
                                        | Shared::WithinFunctionCall {
221
0
                                            ref mut in_interrupted_waker,
222
                                            ..
223
                                        } => {
224
4.15k
                                            *in_interrupted_waker = Some(cx.waker().clone());
225
4.15k
                                            task::Poll::Pending
226
                                        }
227
                                        Shared::MemoryGrowRequired {
228
6
                                            ref memory,
229
6
                                            additional,
230
6
                                        } => {
231
6
                                            // The outer call has made sure that `additional`
232
6
                                            // would fit.
233
6
                                            memory.grow(&mut caller, additional).unwrap();
234
6
                                            *shared_lock = Shared::WithinFunctionCall {
235
6
                                                in_interrupted_waker: Some(cx.waker().clone()),
236
6
                                                memory: SliceRawParts(
237
6
                                                    memory.data_ptr(&caller),
238
6
                                                    memory.data_size(&caller),
239
6
                                                ),
240
6
                                                expected_return_ty,
241
6
                                            };
242
6
                                            task::Poll::Pending
243
                                        }
244
                                        Shared::Return {
245
4.15k
                                            ref mut return_value,
246
4.15k
                                            memory,
247
                                        } => {
248
4.15k
                                            if let Some(
returned2.07k
) = return_value.take() {
249
2.07k
                                                assert_eq!(ret_val.len(), 1);
250
2.07k
                                                ret_val[0] = From::from(returned);
251
                                            } else {
252
2.07k
                                                assert!(ret_val.is_empty());
253
                                            }
254
255
4.15k
                                            *shared_lock = Shared::OutsideFunctionCall { memory };
256
4.15k
                                            task::Poll::Ready(Ok(()))
257
                                        }
258
0
                                        _ => unreachable!(),
259
                                    }
260
8.31k
                                }))
261
4.19k
                            },
_RNCNvMNtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB4_12JitPrototype20from_base_componentss_0Ba_
Line
Count
Source
168
45
                            move |mut caller, params, ret_val| {
169
45
                                // This closure is executed whenever the Wasm VM calls a
170
45
                                // host function.
171
45
                                // While a function call is in progress, only this closure can
172
45
                                // have access to the `wasmtime::Store`. For this reason, we use
173
45
                                // a small communication protocol with the outside.
174
45
175
45
                                // Transition `shared` from `OutsideFunctionCall` to
176
45
                                // `EnteredFunctionCall`.
177
45
                                {
178
45
                                    let mut shared_lock = shared.try_lock().unwrap();
179
45
                                    match mem::replace(&mut *shared_lock, Shared::Poisoned) {
180
44
                                        Shared::OutsideFunctionCall { memory } => {
181
44
                                            *shared_lock = Shared::EnteredFunctionCall {
182
44
                                                function_index,
183
44
                                                // Because the function signature has been
184
44
                                                // validated at initialization, we can safely
185
44
                                                // convert all the parameter types.
186
44
                                                parameters: params
187
44
                                                    .iter()
188
44
                                                    .map(TryFrom::try_from)
189
44
                                                    .collect::<Result<_, _>>()
190
44
                                                    .unwrap(),
191
44
                                                expected_return_ty,
192
44
                                                in_interrupted_waker: None, // Filled below
193
44
                                                memory: SliceRawParts(
194
44
                                                    memory.data_ptr(&caller),
195
44
                                                    memory.data_size(&caller),
196
44
                                                ),
197
44
                                            };
198
44
                                        }
199
                                        Shared::ExecutingStart => {
200
1
                                            return Box::new(future::ready(Err(
201
1
                                                wasmtime::Error::new(
202
1
                                                    NewErr::StartFunctionNotSupported,
203
1
                                                ),
204
1
                                            )));
205
                                        }
206
0
                                        _ => unreachable!(),
207
                                    }
208
                                }
209
210
                                // Return a future that is ready whenever `Shared` contains
211
                                // `Return`.
212
44
                                let shared = shared.clone();
213
44
                                Box::new(future::poll_fn(move |cx| {
214
                                    let mut shared_lock = shared.try_lock().unwrap();
215
                                    match *shared_lock {
216
                                        Shared::EnteredFunctionCall {
217
                                            ref mut in_interrupted_waker,
218
                                            ..
219
                                        }
220
                                        | Shared::WithinFunctionCall {
221
                                            ref mut in_interrupted_waker,
222
                                            ..
223
                                        } => {
224
                                            *in_interrupted_waker = Some(cx.waker().clone());
225
                                            task::Poll::Pending
226
                                        }
227
                                        Shared::MemoryGrowRequired {
228
                                            ref memory,
229
                                            additional,
230
                                        } => {
231
                                            // The outer call has made sure that `additional`
232
                                            // would fit.
233
                                            memory.grow(&mut caller, additional).unwrap();
234
                                            *shared_lock = Shared::WithinFunctionCall {
235
                                                in_interrupted_waker: Some(cx.waker().clone()),
236
                                                memory: SliceRawParts(
237
                                                    memory.data_ptr(&caller),
238
                                                    memory.data_size(&caller),
239
                                                ),
240
                                                expected_return_ty,
241
                                            };
242
                                            task::Poll::Pending
243
                                        }
244
                                        Shared::Return {
245
                                            ref mut return_value,
246
                                            memory,
247
                                        } => {
248
                                            if let Some(returned) = return_value.take() {
249
                                                assert_eq!(ret_val.len(), 1);
250
                                                ret_val[0] = From::from(returned);
251
                                            } else {
252
                                                assert!(ret_val.is_empty());
253
                                            }
254
255
                                            *shared_lock = Shared::OutsideFunctionCall { memory };
256
                                            task::Poll::Ready(Ok(()))
257
                                        }
258
                                        _ => unreachable!(),
259
                                    }
260
44
                                }))
261
45
                            },
_RNCNvMNtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB4_12JitPrototype20from_base_componentss_0Ba_
Line
Count
Source
168
4.15k
                            move |mut caller, params, ret_val| {
169
4.15k
                                // This closure is executed whenever the Wasm VM calls a
170
4.15k
                                // host function.
171
4.15k
                                // While a function call is in progress, only this closure can
172
4.15k
                                // have access to the `wasmtime::Store`. For this reason, we use
173
4.15k
                                // a small communication protocol with the outside.
174
4.15k
175
4.15k
                                // Transition `shared` from `OutsideFunctionCall` to
176
4.15k
                                // `EnteredFunctionCall`.
177
4.15k
                                {
178
4.15k
                                    let mut shared_lock = shared.try_lock().unwrap();
179
4.15k
                                    match mem::replace(&mut *shared_lock, Shared::Poisoned) {
180
4.15k
                                        Shared::OutsideFunctionCall { memory } => {
181
4.15k
                                            *shared_lock = Shared::EnteredFunctionCall {
182
4.15k
                                                function_index,
183
4.15k
                                                // Because the function signature has been
184
4.15k
                                                // validated at initialization, we can safely
185
4.15k
                                                // convert all the parameter types.
186
4.15k
                                                parameters: params
187
4.15k
                                                    .iter()
188
4.15k
                                                    .map(TryFrom::try_from)
189
4.15k
                                                    .collect::<Result<_, _>>()
190
4.15k
                                                    .unwrap(),
191
4.15k
                                                expected_return_ty,
192
4.15k
                                                in_interrupted_waker: None, // Filled below
193
4.15k
                                                memory: SliceRawParts(
194
4.15k
                                                    memory.data_ptr(&caller),
195
4.15k
                                                    memory.data_size(&caller),
196
4.15k
                                                ),
197
4.15k
                                            };
198
4.15k
                                        }
199
                                        Shared::ExecutingStart => {
200
0
                                            return Box::new(future::ready(Err(
201
0
                                                wasmtime::Error::new(
202
0
                                                    NewErr::StartFunctionNotSupported,
203
0
                                                ),
204
0
                                            )));
205
                                        }
206
0
                                        _ => unreachable!(),
207
                                    }
208
                                }
209
210
                                // Return a future that is ready whenever `Shared` contains
211
                                // `Return`.
212
4.15k
                                let shared = shared.clone();
213
4.15k
                                Box::new(future::poll_fn(move |cx| {
214
                                    let mut shared_lock = shared.try_lock().unwrap();
215
                                    match *shared_lock {
216
                                        Shared::EnteredFunctionCall {
217
                                            ref mut in_interrupted_waker,
218
                                            ..
219
                                        }
220
                                        | Shared::WithinFunctionCall {
221
                                            ref mut in_interrupted_waker,
222
                                            ..
223
                                        } => {
224
                                            *in_interrupted_waker = Some(cx.waker().clone());
225
                                            task::Poll::Pending
226
                                        }
227
                                        Shared::MemoryGrowRequired {
228
                                            ref memory,
229
                                            additional,
230
                                        } => {
231
                                            // The outer call has made sure that `additional`
232
                                            // would fit.
233
                                            memory.grow(&mut caller, additional).unwrap();
234
                                            *shared_lock = Shared::WithinFunctionCall {
235
                                                in_interrupted_waker: Some(cx.waker().clone()),
236
                                                memory: SliceRawParts(
237
                                                    memory.data_ptr(&caller),
238
                                                    memory.data_size(&caller),
239
                                                ),
240
                                                expected_return_ty,
241
                                            };
242
                                            task::Poll::Pending
243
                                        }
244
                                        Shared::Return {
245
                                            ref mut return_value,
246
                                            memory,
247
                                        } => {
248
                                            if let Some(returned) = return_value.take() {
249
                                                assert_eq!(ret_val.len(), 1);
250
                                                ret_val[0] = From::from(returned);
251
                                            } else {
252
                                                assert!(ret_val.is_empty());
253
                                            }
254
255
                                            *shared_lock = Shared::OutsideFunctionCall { memory };
256
                                            task::Poll::Ready(Ok(()))
257
                                        }
258
                                        _ => unreachable!(),
259
                                    }
260
4.15k
                                }))
261
4.15k
                            },
262
905
                        )));
263
905
                    }
264
                    wasmtime::ExternType::Global(_) | wasmtime::ExternType::Table(_) => {
265
0
                        unreachable!() // Should have been checked earlier.
266
                    }
267
59
                    wasmtime::ExternType::Memory(m) => {
268
59
                        if module_import.module() != "env" || module_import.name() != "memory" {
269
1
                            return Err(NewErr::MemoryNotNamedMemory);
270
58
                        }
271
58
272
58
                        // Considering that the memory can only be "env":"memory", and that each
273
58
                        // import has a unique name, this block can't be reached more than once.
274
58
                        debug_assert!(imported_memory.is_none());
275
                        imported_memory = Some(
276
58
                            wasmtime::Memory::new(&mut store, m)
277
58
                                .map_err(|_| 
NewErr::CouldntAllocateMemory0
)
?0
,
Unexecuted instantiation: _RNCNvMNtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB4_12JitPrototype20from_base_componentss0_0Ba_
Unexecuted instantiation: _RNCNvMNtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB4_12JitPrototype20from_base_componentss0_0Ba_
278
                        );
279
58
                        imports.push(wasmtime::Extern::Memory(*imported_memory.as_ref().unwrap()));
280
                    }
281
                };
282
            }
283
84
            imports
284
        };
285
286
        // Calling `wasmtime::Instance::new` executes the `start` function of the module, if any.
287
        // If this `start` function calls into one of the imports, then the import will detect
288
        // that the shared state is `ExecutingStart` and return an error.
289
        // This function call is asynchronous because the `start` function might be asynchronous.
290
        // In principle, `now_or_never()` can be unwrapped because the only way for `start` to
291
        // not be immediately finished is if it enters an import, which immediately returns an
292
        // error. However we return an error anyway, just in case.
293
        // If the `start` function doesn't call any import, then it will go undetected and no
294
        // error will be returned.
295
        // TODO: detect `start` anyway, for consistency with other backends
296
82
        let instance = match future::Future::poll(
297
84
            pin::pin!(wasmtime::Instance::new_async(
298
84
                &mut store,
299
84
                &base_components.module,
300
84
                &imports
301
84
            )),
302
84
            &mut task::Context::from_waker(&noop_waker()),
303
        ) {
304
0
            task::Poll::Pending => return Err(NewErr::StartFunctionNotSupported), // TODO: hacky error value, as the error could also be different
305
82
            task::Poll::Ready(Ok(i)) => i,
306
2
            task::Poll::Ready(Err(err)) => return Err(NewErr::Instantiation(err.to_string())),
307
        };
308
309
        // Now that we are passed the `start` stage, update the state of execution.
310
82
        *shared.lock().unwrap() = Shared::Poisoned;
311
312
82
        let 
exported_memory81
= if let Some(
mem23
) = instance.get_export(&mut store, "memory") {
313
23
            if let Some(
mem22
) = mem.into_memory() {
314
22
                Some(mem)
315
            } else {
316
1
                return Err(NewErr::MemoryIsntMemory);
317
            }
318
        } else {
319
59
            None
320
        };
321
322
81
        let 
memory80
= match (exported_memory, imported_memory) {
323
0
            (Some(_), Some(_)) => return Err(NewErr::TwoMemories),
324
22
            (Some(m), None) => m,
325
58
            (None, Some(m)) => m,
326
1
            (None, None) => return Err(NewErr::NoMemory),
327
        };
328
329
80
        let memory_type = memory.ty(&store);
330
80
331
80
        Ok(JitPrototype {
332
80
            base_components,
333
80
            store,
334
80
            instance,
335
80
            shared,
336
80
            memory,
337
80
            memory_type,
338
80
        })
339
85
    }
_RNvMNtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB2_12JitPrototype20from_base_components
Line
Count
Source
139
61
    fn from_base_components(base_components: BaseComponents) -> Result<Self, NewErr> {
140
61
        let mut store = wasmtime::Store::new(base_components.module.engine(), ());
141
61
142
61
        let mut imported_memory = None;
143
61
        let shared = Arc::new(Mutex::new(Shared::ExecutingStart));
144
145
        // Building the list of symbols that the Wasm VM is able to use.
146
60
        let imports = {
147
61
            let mut imports = Vec::with_capacity(base_components.module.imports().len());
148
244
            for (module_import, resolved_function) in base_components
149
61
                .module
150
61
                .imports()
151
61
                .zip(base_components.resolved_imports.iter())
152
            {
153
244
                match module_import.ty() {
154
209
                    wasmtime::ExternType::Func(func_type) => {
155
209
                        let function_index = resolved_function.unwrap();
156
209
                        let shared = shared.clone();
157
209
158
209
                        // Obtain `expected_return_ty`. We know that the type is supported due to
159
209
                        // the signature check earlier.
160
209
                        let expected_return_ty = func_type
161
209
                            .results()
162
209
                            .next()
163
209
                            .map(|v| ValueType::try_from(v).unwrap());
164
209
165
209
                        imports.push(wasmtime::Extern::Func(wasmtime::Func::new_async(
166
209
                            &mut store,
167
209
                            func_type,
168
209
                            move |mut caller, params, ret_val| {
169
                                // This closure is executed whenever the Wasm VM calls a
170
                                // host function.
171
                                // While a function call is in progress, only this closure can
172
                                // have access to the `wasmtime::Store`. For this reason, we use
173
                                // a small communication protocol with the outside.
174
175
                                // Transition `shared` from `OutsideFunctionCall` to
176
                                // `EnteredFunctionCall`.
177
                                {
178
                                    let mut shared_lock = shared.try_lock().unwrap();
179
                                    match mem::replace(&mut *shared_lock, Shared::Poisoned) {
180
                                        Shared::OutsideFunctionCall { memory } => {
181
                                            *shared_lock = Shared::EnteredFunctionCall {
182
                                                function_index,
183
                                                // Because the function signature has been
184
                                                // validated at initialization, we can safely
185
                                                // convert all the parameter types.
186
                                                parameters: params
187
                                                    .iter()
188
                                                    .map(TryFrom::try_from)
189
                                                    .collect::<Result<_, _>>()
190
                                                    .unwrap(),
191
                                                expected_return_ty,
192
                                                in_interrupted_waker: None, // Filled below
193
                                                memory: SliceRawParts(
194
                                                    memory.data_ptr(&caller),
195
                                                    memory.data_size(&caller),
196
                                                ),
197
                                            };
198
                                        }
199
                                        Shared::ExecutingStart => {
200
                                            return Box::new(future::ready(Err(
201
                                                wasmtime::Error::new(
202
                                                    NewErr::StartFunctionNotSupported,
203
                                                ),
204
                                            )));
205
                                        }
206
                                        _ => unreachable!(),
207
                                    }
208
                                }
209
210
                                // Return a future that is ready whenever `Shared` contains
211
                                // `Return`.
212
                                let shared = shared.clone();
213
                                Box::new(future::poll_fn(move |cx| {
214
                                    let mut shared_lock = shared.try_lock().unwrap();
215
                                    match *shared_lock {
216
                                        Shared::EnteredFunctionCall {
217
                                            ref mut in_interrupted_waker,
218
                                            ..
219
                                        }
220
                                        | Shared::WithinFunctionCall {
221
                                            ref mut in_interrupted_waker,
222
                                            ..
223
                                        } => {
224
                                            *in_interrupted_waker = Some(cx.waker().clone());
225
                                            task::Poll::Pending
226
                                        }
227
                                        Shared::MemoryGrowRequired {
228
                                            ref memory,
229
                                            additional,
230
                                        } => {
231
                                            // The outer call has made sure that `additional`
232
                                            // would fit.
233
                                            memory.grow(&mut caller, additional).unwrap();
234
                                            *shared_lock = Shared::WithinFunctionCall {
235
                                                in_interrupted_waker: Some(cx.waker().clone()),
236
                                                memory: SliceRawParts(
237
                                                    memory.data_ptr(&caller),
238
                                                    memory.data_size(&caller),
239
                                                ),
240
                                                expected_return_ty,
241
                                            };
242
                                            task::Poll::Pending
243
                                        }
244
                                        Shared::Return {
245
                                            ref mut return_value,
246
                                            memory,
247
                                        } => {
248
                                            if let Some(returned) = return_value.take() {
249
                                                assert_eq!(ret_val.len(), 1);
250
                                                ret_val[0] = From::from(returned);
251
                                            } else {
252
                                                assert!(ret_val.is_empty());
253
                                            }
254
255
                                            *shared_lock = Shared::OutsideFunctionCall { memory };
256
                                            task::Poll::Ready(Ok(()))
257
                                        }
258
                                        _ => unreachable!(),
259
                                    }
260
                                }))
261
209
                            },
262
209
                        )));
263
209
                    }
264
                    wasmtime::ExternType::Global(_) | wasmtime::ExternType::Table(_) => {
265
0
                        unreachable!() // Should have been checked earlier.
266
                    }
267
35
                    wasmtime::ExternType::Memory(m) => {
268
35
                        if module_import.module() != "env" || module_import.name() != "memory" {
269
1
                            return Err(NewErr::MemoryNotNamedMemory);
270
34
                        }
271
34
272
34
                        // Considering that the memory can only be "env":"memory", and that each
273
34
                        // import has a unique name, this block can't be reached more than once.
274
34
                        debug_assert!(imported_memory.is_none());
275
                        imported_memory = Some(
276
34
                            wasmtime::Memory::new(&mut store, m)
277
34
                                .map_err(|_| NewErr::CouldntAllocateMemory)
?0
,
278
                        );
279
34
                        imports.push(wasmtime::Extern::Memory(*imported_memory.as_ref().unwrap()));
280
                    }
281
                };
282
            }
283
60
            imports
284
        };
285
286
        // Calling `wasmtime::Instance::new` executes the `start` function of the module, if any.
287
        // If this `start` function calls into one of the imports, then the import will detect
288
        // that the shared state is `ExecutingStart` and return an error.
289
        // This function call is asynchronous because the `start` function might be asynchronous.
290
        // In principle, `now_or_never()` can be unwrapped because the only way for `start` to
291
        // not be immediately finished is if it enters an import, which immediately returns an
292
        // error. However we return an error anyway, just in case.
293
        // If the `start` function doesn't call any import, then it will go undetected and no
294
        // error will be returned.
295
        // TODO: detect `start` anyway, for consistency with other backends
296
58
        let instance = match future::Future::poll(
297
60
            pin::pin!(wasmtime::Instance::new_async(
298
60
                &mut store,
299
60
                &base_components.module,
300
60
                &imports
301
60
            )),
302
60
            &mut task::Context::from_waker(&noop_waker()),
303
        ) {
304
0
            task::Poll::Pending => return Err(NewErr::StartFunctionNotSupported), // TODO: hacky error value, as the error could also be different
305
58
            task::Poll::Ready(Ok(i)) => i,
306
2
            task::Poll::Ready(Err(err)) => return Err(NewErr::Instantiation(err.to_string())),
307
        };
308
309
        // Now that we are passed the `start` stage, update the state of execution.
310
58
        *shared.lock().unwrap() = Shared::Poisoned;
311
312
58
        let 
exported_memory57
= if let Some(
mem23
) = instance.get_export(&mut store, "memory") {
313
23
            if let Some(
mem22
) = mem.into_memory() {
314
22
                Some(mem)
315
            } else {
316
1
                return Err(NewErr::MemoryIsntMemory);
317
            }
318
        } else {
319
35
            None
320
        };
321
322
57
        let 
memory56
= match (exported_memory, imported_memory) {
323
0
            (Some(_), Some(_)) => return Err(NewErr::TwoMemories),
324
22
            (Some(m), None) => m,
325
34
            (None, Some(m)) => m,
326
1
            (None, None) => return Err(NewErr::NoMemory),
327
        };
328
329
56
        let memory_type = memory.ty(&store);
330
56
331
56
        Ok(JitPrototype {
332
56
            base_components,
333
56
            store,
334
56
            instance,
335
56
            shared,
336
56
            memory,
337
56
            memory_type,
338
56
        })
339
61
    }
_RNvMNtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB2_12JitPrototype20from_base_components
Line
Count
Source
139
24
    fn from_base_components(base_components: BaseComponents) -> Result<Self, NewErr> {
140
24
        let mut store = wasmtime::Store::new(base_components.module.engine(), ());
141
24
142
24
        let mut imported_memory = None;
143
24
        let shared = Arc::new(Mutex::new(Shared::ExecutingStart));
144
145
        // Building the list of symbols that the Wasm VM is able to use.
146
24
        let imports = {
147
24
            let mut imports = Vec::with_capacity(base_components.module.imports().len());
148
720
            for (module_import, resolved_function) in base_components
149
24
                .module
150
24
                .imports()
151
24
                .zip(base_components.resolved_imports.iter())
152
            {
153
720
                match module_import.ty() {
154
696
                    wasmtime::ExternType::Func(func_type) => {
155
696
                        let function_index = resolved_function.unwrap();
156
696
                        let shared = shared.clone();
157
696
158
696
                        // Obtain `expected_return_ty`. We know that the type is supported due to
159
696
                        // the signature check earlier.
160
696
                        let expected_return_ty = func_type
161
696
                            .results()
162
696
                            .next()
163
696
                            .map(|v| ValueType::try_from(v).unwrap());
164
696
165
696
                        imports.push(wasmtime::Extern::Func(wasmtime::Func::new_async(
166
696
                            &mut store,
167
696
                            func_type,
168
696
                            move |mut caller, params, ret_val| {
169
                                // This closure is executed whenever the Wasm VM calls a
170
                                // host function.
171
                                // While a function call is in progress, only this closure can
172
                                // have access to the `wasmtime::Store`. For this reason, we use
173
                                // a small communication protocol with the outside.
174
175
                                // Transition `shared` from `OutsideFunctionCall` to
176
                                // `EnteredFunctionCall`.
177
                                {
178
                                    let mut shared_lock = shared.try_lock().unwrap();
179
                                    match mem::replace(&mut *shared_lock, Shared::Poisoned) {
180
                                        Shared::OutsideFunctionCall { memory } => {
181
                                            *shared_lock = Shared::EnteredFunctionCall {
182
                                                function_index,
183
                                                // Because the function signature has been
184
                                                // validated at initialization, we can safely
185
                                                // convert all the parameter types.
186
                                                parameters: params
187
                                                    .iter()
188
                                                    .map(TryFrom::try_from)
189
                                                    .collect::<Result<_, _>>()
190
                                                    .unwrap(),
191
                                                expected_return_ty,
192
                                                in_interrupted_waker: None, // Filled below
193
                                                memory: SliceRawParts(
194
                                                    memory.data_ptr(&caller),
195
                                                    memory.data_size(&caller),
196
                                                ),
197
                                            };
198
                                        }
199
                                        Shared::ExecutingStart => {
200
                                            return Box::new(future::ready(Err(
201
                                                wasmtime::Error::new(
202
                                                    NewErr::StartFunctionNotSupported,
203
                                                ),
204
                                            )));
205
                                        }
206
                                        _ => unreachable!(),
207
                                    }
208
                                }
209
210
                                // Return a future that is ready whenever `Shared` contains
211
                                // `Return`.
212
                                let shared = shared.clone();
213
                                Box::new(future::poll_fn(move |cx| {
214
                                    let mut shared_lock = shared.try_lock().unwrap();
215
                                    match *shared_lock {
216
                                        Shared::EnteredFunctionCall {
217
                                            ref mut in_interrupted_waker,
218
                                            ..
219
                                        }
220
                                        | Shared::WithinFunctionCall {
221
                                            ref mut in_interrupted_waker,
222
                                            ..
223
                                        } => {
224
                                            *in_interrupted_waker = Some(cx.waker().clone());
225
                                            task::Poll::Pending
226
                                        }
227
                                        Shared::MemoryGrowRequired {
228
                                            ref memory,
229
                                            additional,
230
                                        } => {
231
                                            // The outer call has made sure that `additional`
232
                                            // would fit.
233
                                            memory.grow(&mut caller, additional).unwrap();
234
                                            *shared_lock = Shared::WithinFunctionCall {
235
                                                in_interrupted_waker: Some(cx.waker().clone()),
236
                                                memory: SliceRawParts(
237
                                                    memory.data_ptr(&caller),
238
                                                    memory.data_size(&caller),
239
                                                ),
240
                                                expected_return_ty,
241
                                            };
242
                                            task::Poll::Pending
243
                                        }
244
                                        Shared::Return {
245
                                            ref mut return_value,
246
                                            memory,
247
                                        } => {
248
                                            if let Some(returned) = return_value.take() {
249
                                                assert_eq!(ret_val.len(), 1);
250
                                                ret_val[0] = From::from(returned);
251
                                            } else {
252
                                                assert!(ret_val.is_empty());
253
                                            }
254
255
                                            *shared_lock = Shared::OutsideFunctionCall { memory };
256
                                            task::Poll::Ready(Ok(()))
257
                                        }
258
                                        _ => unreachable!(),
259
                                    }
260
                                }))
261
696
                            },
262
696
                        )));
263
696
                    }
264
                    wasmtime::ExternType::Global(_) | wasmtime::ExternType::Table(_) => {
265
0
                        unreachable!() // Should have been checked earlier.
266
                    }
267
24
                    wasmtime::ExternType::Memory(m) => {
268
24
                        if module_import.module() != "env" || module_import.name() != "memory" {
269
0
                            return Err(NewErr::MemoryNotNamedMemory);
270
24
                        }
271
24
272
24
                        // Considering that the memory can only be "env":"memory", and that each
273
24
                        // import has a unique name, this block can't be reached more than once.
274
24
                        debug_assert!(imported_memory.is_none());
275
                        imported_memory = Some(
276
24
                            wasmtime::Memory::new(&mut store, m)
277
24
                                .map_err(|_| NewErr::CouldntAllocateMemory)
?0
,
278
                        );
279
24
                        imports.push(wasmtime::Extern::Memory(*imported_memory.as_ref().unwrap()));
280
                    }
281
                };
282
            }
283
24
            imports
284
        };
285
286
        // Calling `wasmtime::Instance::new` executes the `start` function of the module, if any.
287
        // If this `start` function calls into one of the imports, then the import will detect
288
        // that the shared state is `ExecutingStart` and return an error.
289
        // This function call is asynchronous because the `start` function might be asynchronous.
290
        // In principle, `now_or_never()` can be unwrapped because the only way for `start` to
291
        // not be immediately finished is if it enters an import, which immediately returns an
292
        // error. However we return an error anyway, just in case.
293
        // If the `start` function doesn't call any import, then it will go undetected and no
294
        // error will be returned.
295
        // TODO: detect `start` anyway, for consistency with other backends
296
24
        let instance = match future::Future::poll(
297
24
            pin::pin!(wasmtime::Instance::new_async(
298
24
                &mut store,
299
24
                &base_components.module,
300
24
                &imports
301
24
            )),
302
24
            &mut task::Context::from_waker(&noop_waker()),
303
        ) {
304
0
            task::Poll::Pending => return Err(NewErr::StartFunctionNotSupported), // TODO: hacky error value, as the error could also be different
305
24
            task::Poll::Ready(Ok(i)) => i,
306
0
            task::Poll::Ready(Err(err)) => return Err(NewErr::Instantiation(err.to_string())),
307
        };
308
309
        // Now that we are passed the `start` stage, update the state of execution.
310
24
        *shared.lock().unwrap() = Shared::Poisoned;
311
312
24
        let exported_memory = if let Some(
mem0
) = instance.get_export(&mut store, "memory") {
313
0
            if let Some(mem) = mem.into_memory() {
314
0
                Some(mem)
315
            } else {
316
0
                return Err(NewErr::MemoryIsntMemory);
317
            }
318
        } else {
319
24
            None
320
        };
321
322
24
        let memory = match (exported_memory, imported_memory) {
323
0
            (Some(_), Some(_)) => return Err(NewErr::TwoMemories),
324
0
            (Some(m), None) => m,
325
24
            (None, Some(m)) => m,
326
0
            (None, None) => return Err(NewErr::NoMemory),
327
        };
328
329
24
        let memory_type = memory.ty(&store);
330
24
331
24
        Ok(JitPrototype {
332
24
            base_components,
333
24
            store,
334
24
            instance,
335
24
            shared,
336
24
            memory,
337
24
            memory_type,
338
24
        })
339
24
    }
340
341
    /// See [`super::VirtualMachinePrototype::global_value`].
342
54
    pub fn global_value(&mut self, name: &str) -> Result<u32, GlobalValueErr> {
343
54
        match self.instance.get_export(&mut self.store, name) {
344
51
            Some(wasmtime::Extern::Global(g)) => match g.get(&mut self.store) {
345
50
                wasmtime::Val::I32(v) => Ok(u32::from_ne_bytes(v.to_ne_bytes())),
346
1
                _ => Err(GlobalValueErr::Invalid),
347
            },
348
3
            _ => Err(GlobalValueErr::NotFound),
349
        }
350
54
    }
_RNvMNtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB2_12JitPrototype12global_value
Line
Count
Source
342
31
    pub fn global_value(&mut self, name: &str) -> Result<u32, GlobalValueErr> {
343
31
        match self.instance.get_export(&mut self.store, name) {
344
28
            Some(wasmtime::Extern::Global(g)) => match g.get(&mut self.store) {
345
27
                wasmtime::Val::I32(v) => Ok(u32::from_ne_bytes(v.to_ne_bytes())),
346
1
                _ => Err(GlobalValueErr::Invalid),
347
            },
348
3
            _ => Err(GlobalValueErr::NotFound),
349
        }
350
31
    }
_RNvMNtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB2_12JitPrototype12global_value
Line
Count
Source
342
23
    pub fn global_value(&mut self, name: &str) -> Result<u32, GlobalValueErr> {
343
23
        match self.instance.get_export(&mut self.store, name) {
344
23
            Some(wasmtime::Extern::Global(g)) => match g.get(&mut self.store) {
345
23
                wasmtime::Val::I32(v) => Ok(u32::from_ne_bytes(v.to_ne_bytes())),
346
0
                _ => Err(GlobalValueErr::Invalid),
347
            },
348
0
            _ => Err(GlobalValueErr::NotFound),
349
        }
350
23
    }
351
352
    /// See [`super::VirtualMachinePrototype::memory_max_pages`].
353
49
    pub fn memory_max_pages(&self) -> Option<HeapPages> {
354
49
        let 
num2
= self.memory.ty(&self.store).maximum()
?47
;
355
2
        match u32::try_from(num) {
356
2
            Ok(n) => Some(HeapPages::new(n)),
357
            // If `num` doesn't fit in a `u32`, we return `None` to mean "infinite".
358
0
            Err(_) => None,
359
        }
360
49
    }
_RNvMNtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB2_12JitPrototype16memory_max_pages
Line
Count
Source
353
26
    pub fn memory_max_pages(&self) -> Option<HeapPages> {
354
26
        let 
num2
= self.memory.ty(&self.store).maximum()
?24
;
355
2
        match u32::try_from(num) {
356
2
            Ok(n) => Some(HeapPages::new(n)),
357
            // If `num` doesn't fit in a `u32`, we return `None` to mean "infinite".
358
0
            Err(_) => None,
359
        }
360
26
    }
_RNvMNtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB2_12JitPrototype16memory_max_pages
Line
Count
Source
353
23
    pub fn memory_max_pages(&self) -> Option<HeapPages> {
354
23
        let 
num0
= self.memory.ty(&self.store).maximum()?;
355
0
        match u32::try_from(num) {
356
0
            Ok(n) => Some(HeapPages::new(n)),
357
            // If `num` doesn't fit in a `u32`, we return `None` to mean "infinite".
358
0
            Err(_) => None,
359
        }
360
23
    }
361
362
    /// See [`super::VirtualMachinePrototype::prepare`].
363
37
    pub fn prepare(self) -> Prepare {
364
37
        Prepare { inner: self }
365
37
    }
_RNvMNtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB2_12JitPrototype7prepare
Line
Count
Source
363
36
    pub fn prepare(self) -> Prepare {
364
36
        Prepare { inner: self }
365
36
    }
_RNvMNtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB2_12JitPrototype7prepare
Line
Count
Source
363
1
    pub fn prepare(self) -> Prepare {
364
1
        Prepare { inner: self }
365
1
    }
366
}
367
368
impl Clone for JitPrototype {
369
1
    fn clone(&self) -> Self {
370
1
        // `from_base_components` is deterministic: either it errors all the time or it never
371
1
        // errors. Since we've called it before and it didn't error, we know that it will also
372
1
        // not error.
373
1
        // The only exception is `NewErr::CouldntAllocateMemory`, but lack of memory is always an
374
1
        // acceptable reason to panic.
375
1
        JitPrototype::from_base_components(BaseComponents {
376
1
            module: self.base_components.module.clone(),
377
1
            resolved_imports: self.base_components.resolved_imports.clone(),
378
1
        })
379
1
        .unwrap()
380
1
    }
Unexecuted instantiation: _RNvXs_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB4_12JitPrototypeNtNtCsaYZPK01V26L_4core5clone5Clone5clone
_RNvXs_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB4_12JitPrototypeNtNtCsaYZPK01V26L_4core5clone5Clone5clone
Line
Count
Source
369
1
    fn clone(&self) -> Self {
370
1
        // `from_base_components` is deterministic: either it errors all the time or it never
371
1
        // errors. Since we've called it before and it didn't error, we know that it will also
372
1
        // not error.
373
1
        // The only exception is `NewErr::CouldntAllocateMemory`, but lack of memory is always an
374
1
        // acceptable reason to panic.
375
1
        JitPrototype::from_base_components(BaseComponents {
376
1
            module: self.base_components.module.clone(),
377
1
            resolved_imports: self.base_components.resolved_imports.clone(),
378
1
        })
379
1
        .unwrap()
380
1
    }
381
}
382
383
impl fmt::Debug for JitPrototype {
384
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
385
0
        f.debug_tuple("JitPrototype").finish()
386
0
    }
Unexecuted instantiation: _RNvXs0_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB5_12JitPrototypeNtNtCsaYZPK01V26L_4core3fmt5Debug3fmt
Unexecuted instantiation: _RNvXs0_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB5_12JitPrototypeNtNtCsaYZPK01V26L_4core3fmt5Debug3fmt
387
}
388
389
/// See [`super::Prepare`].
390
pub struct Prepare {
391
    inner: JitPrototype,
392
}
393
394
impl Prepare {
395
    /// See [`super::Prepare::into_prototype`].
396
1
    pub fn into_prototype(self) -> JitPrototype {
397
1
        // Since the creation has succeeded before, there's no reason why it would fail now.
398
1
        JitPrototype::from_base_components(self.inner.base_components).unwrap()
399
1
    }
_RNvMs1_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB5_7Prepare14into_prototype
Line
Count
Source
396
1
    pub fn into_prototype(self) -> JitPrototype {
397
1
        // Since the creation has succeeded before, there's no reason why it would fail now.
398
1
        JitPrototype::from_base_components(self.inner.base_components).unwrap()
399
1
    }
Unexecuted instantiation: _RNvMs1_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB5_7Prepare14into_prototype
400
401
    /// See [`super::Prepare::memory_size`].
402
42
    pub fn memory_size(&self) -> HeapPages {
403
42
        let heap_pages = self.inner.memory.size(&self.inner.store);
404
42
        HeapPages::new(u32::try_from(heap_pages).unwrap())
405
42
    }
_RNvMs1_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB5_7Prepare11memory_size
Line
Count
Source
402
40
    pub fn memory_size(&self) -> HeapPages {
403
40
        let heap_pages = self.inner.memory.size(&self.inner.store);
404
40
        HeapPages::new(u32::try_from(heap_pages).unwrap())
405
40
    }
_RNvMs1_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB5_7Prepare11memory_size
Line
Count
Source
402
2
    pub fn memory_size(&self) -> HeapPages {
403
2
        let heap_pages = self.inner.memory.size(&self.inner.store);
404
2
        HeapPages::new(u32::try_from(heap_pages).unwrap())
405
2
    }
406
407
    /// See [`super::Prepare::read_memory`].
408
4
    pub fn read_memory(
409
4
        &'_ self,
410
4
        offset: u32,
411
4
        size: u32,
412
4
    ) -> Result<impl AsRef<[u8]> + '_, OutOfBoundsError> {
413
4
        let memory_slice = self.inner.memory.data(&self.inner.store);
414
415
4
        let start = usize::try_from(offset).map_err(|_| 
OutOfBoundsError0
)
?0
;
Unexecuted instantiation: _RNCNvMs1_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB7_7Prepare11read_memory0Bd_
Unexecuted instantiation: _RNCNvMs1_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB7_7Prepare11read_memory0Bd_
416
4
        let end = start
417
4
            .checked_add(usize::try_from(size).map_err(|_| 
OutOfBoundsError0
)
?0
)
Unexecuted instantiation: _RNCNvMs1_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB7_7Prepare11read_memorys_0Bd_
Unexecuted instantiation: _RNCNvMs1_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB7_7Prepare11read_memorys_0Bd_
418
4
            .ok_or(OutOfBoundsError)
?0
;
419
420
4
        if end > memory_slice.len() {
421
0
            return Err(OutOfBoundsError);
422
4
        }
423
4
424
4
        Ok(&memory_slice[start..end])
425
4
    }
_RNvMs1_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB5_7Prepare11read_memory
Line
Count
Source
408
4
    pub fn read_memory(
409
4
        &'_ self,
410
4
        offset: u32,
411
4
        size: u32,
412
4
    ) -> Result<impl AsRef<[u8]> + '_, OutOfBoundsError> {
413
4
        let memory_slice = self.inner.memory.data(&self.inner.store);
414
415
4
        let start = usize::try_from(offset).map_err(|_| OutOfBoundsError)
?0
;
416
4
        let end = start
417
4
            .checked_add(usize::try_from(size).map_err(|_| OutOfBoundsError)
?0
)
418
4
            .ok_or(OutOfBoundsError)
?0
;
419
420
4
        if end > memory_slice.len() {
421
0
            return Err(OutOfBoundsError);
422
4
        }
423
4
424
4
        Ok(&memory_slice[start..end])
425
4
    }
Unexecuted instantiation: _RNvMs1_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB5_7Prepare11read_memory
426
427
    /// See [`super::Prepare::write_memory`].
428
40
    pub fn write_memory(&mut self, offset: u32, value: &[u8]) -> Result<(), OutOfBoundsError> {
429
40
        let memory_slice = self.inner.memory.data_mut(&mut self.inner.store);
430
431
40
        let start = usize::try_from(offset).map_err(|_| 
OutOfBoundsError0
)
?0
;
Unexecuted instantiation: _RNCNvMs1_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB7_7Prepare12write_memory0Bd_
Unexecuted instantiation: _RNCNvMs1_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB7_7Prepare12write_memory0Bd_
432
40
        let end = start.checked_add(value.len()).ok_or(OutOfBoundsError)
?0
;
433
434
40
        if end > memory_slice.len() {
435
0
            return Err(OutOfBoundsError);
436
40
        }
437
40
438
40
        if !value.is_empty() {
439
35
            memory_slice[start..end].copy_from_slice(value);
440
35
        }
5
441
442
40
        Ok(())
443
40
    }
_RNvMs1_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB5_7Prepare12write_memory
Line
Count
Source
428
39
    pub fn write_memory(&mut self, offset: u32, value: &[u8]) -> Result<(), OutOfBoundsError> {
429
39
        let memory_slice = self.inner.memory.data_mut(&mut self.inner.store);
430
431
39
        let start = usize::try_from(offset).map_err(|_| OutOfBoundsError)
?0
;
432
39
        let end = start.checked_add(value.len()).ok_or(OutOfBoundsError)
?0
;
433
434
39
        if end > memory_slice.len() {
435
0
            return Err(OutOfBoundsError);
436
39
        }
437
39
438
39
        if !value.is_empty() {
439
34
            memory_slice[start..end].copy_from_slice(value);
440
34
        }
5
441
442
39
        Ok(())
443
39
    }
_RNvMs1_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB5_7Prepare12write_memory
Line
Count
Source
428
1
    pub fn write_memory(&mut self, offset: u32, value: &[u8]) -> Result<(), OutOfBoundsError> {
429
1
        let memory_slice = self.inner.memory.data_mut(&mut self.inner.store);
430
431
1
        let start = usize::try_from(offset).map_err(|_| OutOfBoundsError)
?0
;
432
1
        let end = start.checked_add(value.len()).ok_or(OutOfBoundsError)
?0
;
433
434
1
        if end > memory_slice.len() {
435
0
            return Err(OutOfBoundsError);
436
1
        }
437
1
438
1
        if !value.is_empty() {
439
1
            memory_slice[start..end].copy_from_slice(value);
440
1
        }
0
441
442
1
        Ok(())
443
1
    }
444
445
    /// See [`super::Prepare::grow_memory`].
446
28
    pub fn grow_memory(&mut self, additional: HeapPages) -> Result<(), OutOfBoundsError> {
447
28
        let additional = u64::from(u32::from(additional));
448
28
        self.inner
449
28
            .memory
450
28
            .grow(&mut self.inner.store, additional)
451
28
            .map_err(|_| 
OutOfBoundsError0
)
?0
;
Unexecuted instantiation: _RNCNvMs1_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB7_7Prepare11grow_memory0Bd_
Unexecuted instantiation: _RNCNvMs1_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB7_7Prepare11grow_memory0Bd_
452
28
        Ok(())
453
28
    }
_RNvMs1_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB5_7Prepare11grow_memory
Line
Count
Source
446
27
    pub fn grow_memory(&mut self, additional: HeapPages) -> Result<(), OutOfBoundsError> {
447
27
        let additional = u64::from(u32::from(additional));
448
27
        self.inner
449
27
            .memory
450
27
            .grow(&mut self.inner.store, additional)
451
27
            .map_err(|_| OutOfBoundsError)
?0
;
452
27
        Ok(())
453
27
    }
_RNvMs1_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB5_7Prepare11grow_memory
Line
Count
Source
446
1
    pub fn grow_memory(&mut self, additional: HeapPages) -> Result<(), OutOfBoundsError> {
447
1
        let additional = u64::from(u32::from(additional));
448
1
        self.inner
449
1
            .memory
450
1
            .grow(&mut self.inner.store, additional)
451
1
            .map_err(|_| OutOfBoundsError)
?0
;
452
1
        Ok(())
453
1
    }
454
455
    /// See [`super::Prepare::start`].
456
35
    pub fn start(
457
35
        mut self,
458
35
        function_name: &str,
459
35
        params: &[WasmValue],
460
35
    ) -> Result<Jit, (StartErr, JitPrototype)> {
461
35
        let 
function_to_call32
= match self
462
35
            .inner
463
35
            .instance
464
35
            .get_export(&mut self.inner.store, function_name)
465
        {
466
33
            Some(export) => match export.into_func() {
467
32
                Some(f) => f,
468
1
                None => return Err((StartErr::NotAFunction, self.inner)),
469
            },
470
2
            None => return Err((StartErr::FunctionNotFound, self.inner)),
471
        };
472
473
        // Try to convert the signature of the function to call, in order to make sure
474
        // that the type of parameters and return value are supported.
475
32
        let Ok(
signature31
) = Signature::try_from(&function_to_call.ty(&self.inner.store)) else {
476
1
            return Err((StartErr::SignatureNotSupported, self.inner));
477
        };
478
479
        // Check the types of the provided parameters.
480
31
        if params.len() != signature.parameters().len() {
481
2
            return Err((StartErr::InvalidParameters, self.inner));
482
29
        }
483
40
        for (obtained, expected) in 
params.iter().zip(signature.parameters())29
{
484
40
            if obtained.ty() != *expected {
485
0
                return Err((StartErr::InvalidParameters, self.inner));
486
40
            }
487
        }
488
489
        // This function only performs all the verifications and preparations, but the call isn't
490
        // actually started here because we might still need to potentially access `store`
491
        // before being in the context of a function handler.
492
493
29
        Ok(Jit {
494
29
            base_components: self.inner.base_components,
495
29
            inner: JitInner::NotStarted {
496
29
                store: self.inner.store,
497
29
                function_to_call,
498
40
                params: params.iter().map(|v| (*v).into()).collect::<Vec<_>>(),
_RNCNvMs1_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB7_7Prepare5start0Bd_
Line
Count
Source
498
38
                params: params.iter().map(|v| (*v).into()).collect::<Vec<_>>(),
_RNCNvMs1_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB7_7Prepare5start0Bd_
Line
Count
Source
498
2
                params: params.iter().map(|v| (*v).into()).collect::<Vec<_>>(),
499
29
            },
500
29
            shared: self.inner.shared,
501
29
            memory: self.inner.memory,
502
29
            memory_type: self.inner.memory_type,
503
29
        })
504
35
    }
_RNvMs1_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB5_7Prepare5start
Line
Count
Source
456
34
    pub fn start(
457
34
        mut self,
458
34
        function_name: &str,
459
34
        params: &[WasmValue],
460
34
    ) -> Result<Jit, (StartErr, JitPrototype)> {
461
34
        let 
function_to_call31
= match self
462
34
            .inner
463
34
            .instance
464
34
            .get_export(&mut self.inner.store, function_name)
465
        {
466
32
            Some(export) => match export.into_func() {
467
31
                Some(f) => f,
468
1
                None => return Err((StartErr::NotAFunction, self.inner)),
469
            },
470
2
            None => return Err((StartErr::FunctionNotFound, self.inner)),
471
        };
472
473
        // Try to convert the signature of the function to call, in order to make sure
474
        // that the type of parameters and return value are supported.
475
31
        let Ok(
signature30
) = Signature::try_from(&function_to_call.ty(&self.inner.store)) else {
476
1
            return Err((StartErr::SignatureNotSupported, self.inner));
477
        };
478
479
        // Check the types of the provided parameters.
480
30
        if params.len() != signature.parameters().len() {
481
2
            return Err((StartErr::InvalidParameters, self.inner));
482
28
        }
483
38
        for (obtained, expected) in 
params.iter().zip(signature.parameters())28
{
484
38
            if obtained.ty() != *expected {
485
0
                return Err((StartErr::InvalidParameters, self.inner));
486
38
            }
487
        }
488
489
        // This function only performs all the verifications and preparations, but the call isn't
490
        // actually started here because we might still need to potentially access `store`
491
        // before being in the context of a function handler.
492
493
28
        Ok(Jit {
494
28
            base_components: self.inner.base_components,
495
28
            inner: JitInner::NotStarted {
496
28
                store: self.inner.store,
497
28
                function_to_call,
498
28
                params: params.iter().map(|v| (*v).into()).collect::<Vec<_>>(),
499
28
            },
500
28
            shared: self.inner.shared,
501
28
            memory: self.inner.memory,
502
28
            memory_type: self.inner.memory_type,
503
28
        })
504
34
    }
_RNvMs1_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB5_7Prepare5start
Line
Count
Source
456
1
    pub fn start(
457
1
        mut self,
458
1
        function_name: &str,
459
1
        params: &[WasmValue],
460
1
    ) -> Result<Jit, (StartErr, JitPrototype)> {
461
1
        let function_to_call = match self
462
1
            .inner
463
1
            .instance
464
1
            .get_export(&mut self.inner.store, function_name)
465
        {
466
1
            Some(export) => match export.into_func() {
467
1
                Some(f) => f,
468
0
                None => return Err((StartErr::NotAFunction, self.inner)),
469
            },
470
0
            None => return Err((StartErr::FunctionNotFound, self.inner)),
471
        };
472
473
        // Try to convert the signature of the function to call, in order to make sure
474
        // that the type of parameters and return value are supported.
475
1
        let Ok(signature) = Signature::try_from(&function_to_call.ty(&self.inner.store)) else {
476
0
            return Err((StartErr::SignatureNotSupported, self.inner));
477
        };
478
479
        // Check the types of the provided parameters.
480
1
        if params.len() != signature.parameters().len() {
481
0
            return Err((StartErr::InvalidParameters, self.inner));
482
1
        }
483
2
        for (obtained, expected) in 
params.iter().zip(signature.parameters())1
{
484
2
            if obtained.ty() != *expected {
485
0
                return Err((StartErr::InvalidParameters, self.inner));
486
2
            }
487
        }
488
489
        // This function only performs all the verifications and preparations, but the call isn't
490
        // actually started here because we might still need to potentially access `store`
491
        // before being in the context of a function handler.
492
493
1
        Ok(Jit {
494
1
            base_components: self.inner.base_components,
495
1
            inner: JitInner::NotStarted {
496
1
                store: self.inner.store,
497
1
                function_to_call,
498
1
                params: params.iter().map(|v| (*v).into()).collect::<Vec<_>>(),
499
1
            },
500
1
            shared: self.inner.shared,
501
1
            memory: self.inner.memory,
502
1
            memory_type: self.inner.memory_type,
503
1
        })
504
1
    }
505
}
506
507
impl fmt::Debug for Prepare {
508
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
509
0
        f.debug_tuple("Prepare").finish()
510
0
    }
Unexecuted instantiation: _RNvXs2_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB5_7PrepareNtNtCsaYZPK01V26L_4core3fmt5Debug3fmt
Unexecuted instantiation: _RNvXs2_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB5_7PrepareNtNtCsaYZPK01V26L_4core3fmt5Debug3fmt
511
}
512
513
/// Data shared between the external API and the functions that `wasmtime` directly invokes.
514
///
515
/// The flow is as follows:
516
///
517
/// - `wasmtime` calls a function that shares access to a `Arc<Mutex<Shared>>`. The `Shared` is in
518
/// the [`Shared::OutsideFunctionCall`] state.
519
/// - This function switches the state to the [`Shared::EnteredFunctionCall`] state and returns
520
/// `Poll::Pending`.
521
/// - This `Pending` gets propagated to the body of [`Jit::run`], which was calling `wasmtime`.
522
/// [`Jit::run`] reads `function_index` and `parameters` to determine what happened, switches the
523
/// state of the `Shared` to [`Shared::WithinFunctionCall`] state, and returns `Poll::Pending`.
524
/// - Here, the user can access the memory, in which case the `Shared` is read. If the user wants
525
/// to grow the memory, the state is switched to [`Shared::MemoryGrowRequired`], then execution
526
/// resumed for the function to perform the growth and transition back to
527
/// [`Shared::WithinFunctionCall`].
528
/// - Later, the state is switched to [`Shared::Return`], and execution is resumed.
529
/// - The function called by `wasmtime` reads the return value and returns `Poll::Ready`.
530
///
531
enum Shared {
532
    Poisoned,
533
    ExecutingStart,
534
    OutsideFunctionCall {
535
        memory: wasmtime::Memory,
536
    },
537
    /// Function handler switches to this state as soon as it is entered, so that the host can
538
    /// pick up this state, extract the function index and parameters, and transition to
539
    /// [`Shared::WithinFunctionCall`].
540
    EnteredFunctionCall {
541
        /// Index of the function currently being called.
542
        function_index: usize,
543
        /// Parameters of the function currently being called.
544
        parameters: Vec<WasmValue>,
545
546
        /// See [`Shared::WithinFunctionCall::memory`].
547
        memory: SliceRawParts,
548
        /// See [`Shared::WithinFunctionCall::expected_return_ty`].
549
        expected_return_ty: Option<ValueType>,
550
        /// See [`Shared::WithinFunctionCall::in_interrupted_waker`].
551
        in_interrupted_waker: Option<task::Waker>,
552
    },
553
    WithinFunctionCall {
554
        /// Pointer and size of the location where the virtual machine memory is located in the
555
        /// host memory. This pointer is invalidated if the memory is grown, which can happen
556
        /// between function calls.
557
        memory: SliceRawParts,
558
559
        /// Type of the return value of the function.
560
        expected_return_ty: Option<ValueType>,
561
562
        /// `Waker` that `wasmtime` has passed to the future that is waiting for `return_value`.
563
        /// This value is most likely not very useful, because [`Jit::run`] always polls the outer
564
        /// future whenever the inner future is known to be ready.
565
        /// However, it would be completely legal for `wasmtime` to not poll the inner future if the
566
        /// `waker` that it has passed (the one stored here) wasn't waken up.
567
        /// This field therefore exists in order to future-proof against this possible optimization
568
        /// that `wasmtime` might perform in the future.
569
        in_interrupted_waker: Option<task::Waker>,
570
    },
571
    MemoryGrowRequired {
572
        memory: wasmtime::Memory,
573
        additional: u64,
574
    },
575
    Return {
576
        /// Value to return to the Wasm code.
577
        return_value: Option<WasmValue>,
578
        memory: wasmtime::Memory,
579
    },
580
}
581
582
/// This idiotic struct and unsafe code are necessary because Rust doesn't implement `Send` and
583
/// `Sync` for raw pointers.
584
#[derive(Copy, Clone)]
585
struct SliceRawParts(*mut u8, usize);
586
unsafe impl Send for SliceRawParts {}
587
unsafe impl Sync for SliceRawParts {}
588
589
/// See [`super::VirtualMachine`].
590
pub struct Jit {
591
    /// Base components that can be used to recreate a prototype later if desired.
592
    base_components: BaseComponents,
593
594
    inner: JitInner,
595
596
    /// Shared between the "outside" and the external functions. See [`Shared`].
597
    shared: Arc<Mutex<Shared>>,
598
599
    /// See [`JitPrototype::memory`].
600
    memory: wasmtime::Memory,
601
602
    /// See [`JitPrototype::memory_type`].
603
    memory_type: wasmtime::MemoryType,
604
}
605
606
enum JitInner {
607
    Poisoned,
608
609
    /// Execution has not started yet.
610
    NotStarted {
611
        store: wasmtime::Store<()>,
612
        function_to_call: wasmtime::Func,
613
        params: Vec<wasmtime::Val>,
614
    },
615
    /// `Future` that drives the execution. Contains an invocation of `wasmtime::Func::call_async`.
616
    Executing(BoxFuture<(wasmtime::Store<()>, ExecOutcomeValue)>),
617
    /// Execution has finished because the future has returned `Poll::Ready` in the past.
618
    Done(wasmtime::Store<()>),
619
}
620
621
type BoxFuture<T> = pin::Pin<Box<dyn future::Future<Output = T> + Send>>;
622
type ExecOutcomeValue = Result<Option<WasmValue>, wasmtime::Error>;
623
624
impl Jit {
625
    /// See [`super::VirtualMachine::run`].
626
4.21k
    pub fn run(&mut self, value: Option<WasmValue>) -> Result<ExecOutcome, RunErr> {
627
4.21k
        // Make sure that `self.inner` is in `JitInner::Executing` start, starting the call if
628
4.21k
        // necessary.
629
4.21k
        match self.inner {
630
            JitInner::Executing(_) => {
631
                // Virtual machine was already executing. Update `Shared` to store the return
632
                // value, so that the function handler picks it up and returns it to `wasmtime`.
633
4.19k
                let mut shared_lock = self.shared.try_lock().unwrap();
634
4.19k
                match mem::replace(&mut *shared_lock, Shared::Poisoned) {
635
                    Shared::WithinFunctionCall {
636
4.19k
                        in_interrupted_waker,
637
4.19k
                        expected_return_ty,
638
4.19k
                        memory,
639
4.19k
                    } => {
640
4.19k
                        let provided_value_ty = value.as_ref().map(|v| 
v.ty()2.10k
);
_RNCNvMs5_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB7_3Jit3run0Bd_
Line
Count
Source
640
28
                        let provided_value_ty = value.as_ref().map(|v| v.ty());
_RNCNvMs5_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB7_3Jit3run0Bd_
Line
Count
Source
640
2.07k
                        let provided_value_ty = value.as_ref().map(|v| v.ty());
641
4.19k
                        if expected_return_ty != provided_value_ty {
642
1
                            *shared_lock = Shared::WithinFunctionCall {
643
1
                                in_interrupted_waker,
644
1
                                expected_return_ty,
645
1
                                memory,
646
1
                            };
647
1
                            return Err(RunErr::BadValueTy {
648
1
                                expected: expected_return_ty,
649
1
                                obtained: provided_value_ty,
650
1
                            });
651
4.19k
                        }
652
4.19k
653
4.19k
                        *shared_lock = Shared::Return {
654
4.19k
                            return_value: value,
655
4.19k
                            memory: self.memory,
656
4.19k
                        };
657
658
4.19k
                        if let Some(waker) = in_interrupted_waker {
659
4.19k
                            waker.wake();
660
4.19k
                        }
0
661
                    }
662
0
                    _ => unreachable!(),
663
                }
664
            }
665
0
            JitInner::Done(_) => return Err(RunErr::Poisoned),
666
0
            JitInner::Poisoned => unreachable!(),
667
            JitInner::NotStarted { .. } => {
668
25
                if value.is_some() {
669
1
                    return Err(RunErr::BadValueTy {
670
1
                        expected: None,
671
1
                        obtained: value.as_ref().map(|v| v.ty()),
_RNCNvMs5_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB7_3Jit3runs_0Bd_
Line
Count
Source
671
1
                        obtained: value.as_ref().map(|v| v.ty()),
Unexecuted instantiation: _RNCNvMs5_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB7_3Jit3runs_0Bd_
672
1
                    });
673
24
                }
674
675
24
                let (function_to_call, params, mut store) =
676
24
                    match mem::replace(&mut self.inner, JitInner::Poisoned) {
677
                        JitInner::NotStarted {
678
24
                            function_to_call,
679
24
                            params,
680
24
                            store,
681
24
                        } => (function_to_call, params, store),
682
0
                        _ => unreachable!(),
683
                    };
684
685
24
                *self.shared.try_lock().unwrap() = Shared::OutsideFunctionCall {
686
24
                    memory: self.memory,
687
24
                };
688
24
689
24
                // Check whether the function to call has a return value.
690
24
                // We made sure when starting that the signature was supported.
691
24
                let has_return_value = Signature::try_from(&function_to_call.ty(&store))
692
24
                    .unwrap()
693
24
                    .return_type()
694
24
                    .is_some();
695
24
696
24
                // Starting the function call.
697
24
                let function_call = Box::pin(async move {
698
24
                    // Prepare an array of results to pass to `wasmtime`. Note that the type doesn't
699
24
                    // have to match the actual return value, only the length.
700
24
                    let mut result = [wasmtime::Val::I32(0)];
701
702
24
                    let 
outcome21
= function_to_call
703
24
                        .call_async(
704
24
                            &mut store,
705
24
                            &params,
706
24
                            &mut result[..(if has_return_value { 
123
} else {
01
})],
707
                        )
708
4.19k
                        .await;
709
710
                    // Execution resumes here when the Wasm code has finished, gracefully or not.
711
20
                    match outcome {
712
19
                        Ok(()) if has_return_value => {
713
19
                            // TODO: could implement TryFrom on wasmtime::Val instead of &wasmtime::Val to avoid borrow here?
714
19
                            (store, Ok(Some((&result[0]).try_into().unwrap())))
715
                        }
716
1
                        Ok(()) => (store, Ok(None)),
717
1
                        Err(err) => (store, Err(err)),
718
                    }
719
24
                
}21
);
_RNCNvMs5_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB7_3Jit3runs0_0Bd_
Line
Count
Source
697
23
                let function_call = Box::pin(async move {
698
23
                    // Prepare an array of results to pass to `wasmtime`. Note that the type doesn't
699
23
                    // have to match the actual return value, only the length.
700
23
                    let mut result = [wasmtime::Val::I32(0)];
701
702
23
                    let 
outcome20
= function_to_call
703
23
                        .call_async(
704
23
                            &mut store,
705
23
                            &params,
706
23
                            &mut result[..(if has_return_value { 
122
} else {
01
})],
707
                        )
708
41
                        .await;
709
710
                    // Execution resumes here when the Wasm code has finished, gracefully or not.
711
19
                    match outcome {
712
18
                        Ok(()) if has_return_value => {
713
18
                            // TODO: could implement TryFrom on wasmtime::Val instead of &wasmtime::Val to avoid borrow here?
714
18
                            (store, Ok(Some((&result[0]).try_into().unwrap())))
715
                        }
716
1
                        Ok(()) => (store, Ok(None)),
717
1
                        Err(err) => (store, Err(err)),
718
                    }
719
20
                });
_RNCNvMs5_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB7_3Jit3runs0_0Bd_
Line
Count
Source
697
1
                let function_call = Box::pin(async move {
698
1
                    // Prepare an array of results to pass to `wasmtime`. Note that the type doesn't
699
1
                    // have to match the actual return value, only the length.
700
1
                    let mut result = [wasmtime::Val::I32(0)];
701
702
1
                    let outcome = function_to_call
703
1
                        .call_async(
704
1
                            &mut store,
705
1
                            &params,
706
1
                            &mut result[..(if has_return_value { 1 } else { 
00
})],
707
                        )
708
4.15k
                        .await;
709
710
                    // Execution resumes here when the Wasm code has finished, gracefully or not.
711
1
                    match outcome {
712
1
                        Ok(()) if has_return_value => {
713
1
                            // TODO: could implement TryFrom on wasmtime::Val instead of &wasmtime::Val to avoid borrow here?
714
1
                            (store, Ok(Some((&result[0]).try_into().unwrap())))
715
                        }
716
0
                        Ok(()) => (store, Ok(None)),
717
0
                        Err(err) => (store, Err(err)),
718
                    }
719
1
                });
720
24
721
24
                self.inner = JitInner::Executing(function_call);
722
            }
723
        };
724
725
        // We made sure that the state is in `Executing`. Now grab the future.
726
4.21k
        let function_call = match &mut self.inner {
727
4.21k
            JitInner::Executing(f) => f,
728
0
            _ => unreachable!(),
729
        };
730
731
        // Resume the coroutine execution.
732
        // The `Future` is polled with a no-op waker. We are in total control of when the
733
        // execution might be able to progress, hence the lack of need for a waker.
734
        match future::Future::poll(
735
4.21k
            function_call.as_mut(),
736
4.21k
            &mut task::Context::from_waker(&noop_waker()),
737
        ) {
738
20
            task::Poll::Ready((store, Ok(val))) => {
739
20
                self.inner = JitInner::Done(store);
740
20
                Ok(ExecOutcome::Finished {
741
20
                    // Since we verify at initialization that the signature of the function to
742
20
                    // call is supported, it is guaranteed that the type of this return value is
743
20
                    // supported too.
744
20
                    return_value: Ok(val),
745
20
                })
746
            }
747
1
            task::Poll::Ready((store, Err(err))) => {
748
1
                self.inner = JitInner::Done(store);
749
1
                Ok(ExecOutcome::Finished {
750
1
                    return_value: Err(Trap(err.to_string())),
751
1
                })
752
            }
753
            task::Poll::Pending => {
754
4.19k
                let mut shared_lock = self.shared.try_lock().unwrap();
755
4.19k
                match mem::replace(&mut *shared_lock, Shared::Poisoned) {
756
                    Shared::EnteredFunctionCall {
757
4.19k
                        function_index,
758
4.19k
                        parameters,
759
4.19k
                        memory,
760
4.19k
                        expected_return_ty,
761
4.19k
                        in_interrupted_waker,
762
4.19k
                    } => {
763
4.19k
                        *shared_lock = Shared::WithinFunctionCall {
764
4.19k
                            memory,
765
4.19k
                            expected_return_ty,
766
4.19k
                            in_interrupted_waker,
767
4.19k
                        };
768
4.19k
769
4.19k
                        Ok(ExecOutcome::Interrupted {
770
4.19k
                            id: function_index,
771
4.19k
                            params: parameters,
772
4.19k
                        })
773
                    }
774
0
                    _ => unreachable!(),
775
                }
776
            }
777
        }
778
4.21k
    }
_RNvMs5_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB5_3Jit3run
Line
Count
Source
626
66
    pub fn run(&mut self, value: Option<WasmValue>) -> Result<ExecOutcome, RunErr> {
627
66
        // Make sure that `self.inner` is in `JitInner::Executing` start, starting the call if
628
66
        // necessary.
629
66
        match self.inner {
630
            JitInner::Executing(_) => {
631
                // Virtual machine was already executing. Update `Shared` to store the return
632
                // value, so that the function handler picks it up and returns it to `wasmtime`.
633
42
                let mut shared_lock = self.shared.try_lock().unwrap();
634
42
                match mem::replace(&mut *shared_lock, Shared::Poisoned) {
635
                    Shared::WithinFunctionCall {
636
42
                        in_interrupted_waker,
637
42
                        expected_return_ty,
638
42
                        memory,
639
42
                    } => {
640
42
                        let provided_value_ty = value.as_ref().map(|v| v.ty());
641
42
                        if expected_return_ty != provided_value_ty {
642
1
                            *shared_lock = Shared::WithinFunctionCall {
643
1
                                in_interrupted_waker,
644
1
                                expected_return_ty,
645
1
                                memory,
646
1
                            };
647
1
                            return Err(RunErr::BadValueTy {
648
1
                                expected: expected_return_ty,
649
1
                                obtained: provided_value_ty,
650
1
                            });
651
41
                        }
652
41
653
41
                        *shared_lock = Shared::Return {
654
41
                            return_value: value,
655
41
                            memory: self.memory,
656
41
                        };
657
658
41
                        if let Some(waker) = in_interrupted_waker {
659
41
                            waker.wake();
660
41
                        }
0
661
                    }
662
0
                    _ => unreachable!(),
663
                }
664
            }
665
0
            JitInner::Done(_) => return Err(RunErr::Poisoned),
666
0
            JitInner::Poisoned => unreachable!(),
667
            JitInner::NotStarted { .. } => {
668
24
                if value.is_some() {
669
1
                    return Err(RunErr::BadValueTy {
670
1
                        expected: None,
671
1
                        obtained: value.as_ref().map(|v| v.ty()),
672
1
                    });
673
23
                }
674
675
23
                let (function_to_call, params, mut store) =
676
23
                    match mem::replace(&mut self.inner, JitInner::Poisoned) {
677
                        JitInner::NotStarted {
678
23
                            function_to_call,
679
23
                            params,
680
23
                            store,
681
23
                        } => (function_to_call, params, store),
682
0
                        _ => unreachable!(),
683
                    };
684
685
23
                *self.shared.try_lock().unwrap() = Shared::OutsideFunctionCall {
686
23
                    memory: self.memory,
687
23
                };
688
23
689
23
                // Check whether the function to call has a return value.
690
23
                // We made sure when starting that the signature was supported.
691
23
                let has_return_value = Signature::try_from(&function_to_call.ty(&store))
692
23
                    .unwrap()
693
23
                    .return_type()
694
23
                    .is_some();
695
23
696
23
                // Starting the function call.
697
23
                let function_call = Box::pin(async move {
698
                    // Prepare an array of results to pass to `wasmtime`. Note that the type doesn't
699
                    // have to match the actual return value, only the length.
700
                    let mut result = [wasmtime::Val::I32(0)];
701
702
                    let outcome = function_to_call
703
                        .call_async(
704
                            &mut store,
705
                            &params,
706
                            &mut result[..(if has_return_value { 1 } else { 0 })],
707
                        )
708
                        .await;
709
710
                    // Execution resumes here when the Wasm code has finished, gracefully or not.
711
                    match outcome {
712
                        Ok(()) if has_return_value => {
713
                            // TODO: could implement TryFrom on wasmtime::Val instead of &wasmtime::Val to avoid borrow here?
714
                            (store, Ok(Some((&result[0]).try_into().unwrap())))
715
                        }
716
                        Ok(()) => (store, Ok(None)),
717
                        Err(err) => (store, Err(err)),
718
                    }
719
23
                });
720
23
721
23
                self.inner = JitInner::Executing(function_call);
722
            }
723
        };
724
725
        // We made sure that the state is in `Executing`. Now grab the future.
726
64
        let function_call = match &mut self.inner {
727
64
            JitInner::Executing(f) => f,
728
0
            _ => unreachable!(),
729
        };
730
731
        // Resume the coroutine execution.
732
        // The `Future` is polled with a no-op waker. We are in total control of when the
733
        // execution might be able to progress, hence the lack of need for a waker.
734
        match future::Future::poll(
735
64
            function_call.as_mut(),
736
64
            &mut task::Context::from_waker(&noop_waker()),
737
        ) {
738
19
            task::Poll::Ready((store, Ok(val))) => {
739
19
                self.inner = JitInner::Done(store);
740
19
                Ok(ExecOutcome::Finished {
741
19
                    // Since we verify at initialization that the signature of the function to
742
19
                    // call is supported, it is guaranteed that the type of this return value is
743
19
                    // supported too.
744
19
                    return_value: Ok(val),
745
19
                })
746
            }
747
1
            task::Poll::Ready((store, Err(err))) => {
748
1
                self.inner = JitInner::Done(store);
749
1
                Ok(ExecOutcome::Finished {
750
1
                    return_value: Err(Trap(err.to_string())),
751
1
                })
752
            }
753
            task::Poll::Pending => {
754
44
                let mut shared_lock = self.shared.try_lock().unwrap();
755
44
                match mem::replace(&mut *shared_lock, Shared::Poisoned) {
756
                    Shared::EnteredFunctionCall {
757
44
                        function_index,
758
44
                        parameters,
759
44
                        memory,
760
44
                        expected_return_ty,
761
44
                        in_interrupted_waker,
762
44
                    } => {
763
44
                        *shared_lock = Shared::WithinFunctionCall {
764
44
                            memory,
765
44
                            expected_return_ty,
766
44
                            in_interrupted_waker,
767
44
                        };
768
44
769
44
                        Ok(ExecOutcome::Interrupted {
770
44
                            id: function_index,
771
44
                            params: parameters,
772
44
                        })
773
                    }
774
0
                    _ => unreachable!(),
775
                }
776
            }
777
        }
778
66
    }
_RNvMs5_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB5_3Jit3run
Line
Count
Source
626
4.15k
    pub fn run(&mut self, value: Option<WasmValue>) -> Result<ExecOutcome, RunErr> {
627
4.15k
        // Make sure that `self.inner` is in `JitInner::Executing` start, starting the call if
628
4.15k
        // necessary.
629
4.15k
        match self.inner {
630
            JitInner::Executing(_) => {
631
                // Virtual machine was already executing. Update `Shared` to store the return
632
                // value, so that the function handler picks it up and returns it to `wasmtime`.
633
4.15k
                let mut shared_lock = self.shared.try_lock().unwrap();
634
4.15k
                match mem::replace(&mut *shared_lock, Shared::Poisoned) {
635
                    Shared::WithinFunctionCall {
636
4.15k
                        in_interrupted_waker,
637
4.15k
                        expected_return_ty,
638
4.15k
                        memory,
639
4.15k
                    } => {
640
4.15k
                        let provided_value_ty = value.as_ref().map(|v| v.ty());
641
4.15k
                        if expected_return_ty != provided_value_ty {
642
0
                            *shared_lock = Shared::WithinFunctionCall {
643
0
                                in_interrupted_waker,
644
0
                                expected_return_ty,
645
0
                                memory,
646
0
                            };
647
0
                            return Err(RunErr::BadValueTy {
648
0
                                expected: expected_return_ty,
649
0
                                obtained: provided_value_ty,
650
0
                            });
651
4.15k
                        }
652
4.15k
653
4.15k
                        *shared_lock = Shared::Return {
654
4.15k
                            return_value: value,
655
4.15k
                            memory: self.memory,
656
4.15k
                        };
657
658
4.15k
                        if let Some(waker) = in_interrupted_waker {
659
4.15k
                            waker.wake();
660
4.15k
                        }
0
661
                    }
662
0
                    _ => unreachable!(),
663
                }
664
            }
665
0
            JitInner::Done(_) => return Err(RunErr::Poisoned),
666
0
            JitInner::Poisoned => unreachable!(),
667
            JitInner::NotStarted { .. } => {
668
1
                if value.is_some() {
669
0
                    return Err(RunErr::BadValueTy {
670
0
                        expected: None,
671
0
                        obtained: value.as_ref().map(|v| v.ty()),
672
0
                    });
673
1
                }
674
675
1
                let (function_to_call, params, mut store) =
676
1
                    match mem::replace(&mut self.inner, JitInner::Poisoned) {
677
                        JitInner::NotStarted {
678
1
                            function_to_call,
679
1
                            params,
680
1
                            store,
681
1
                        } => (function_to_call, params, store),
682
0
                        _ => unreachable!(),
683
                    };
684
685
1
                *self.shared.try_lock().unwrap() = Shared::OutsideFunctionCall {
686
1
                    memory: self.memory,
687
1
                };
688
1
689
1
                // Check whether the function to call has a return value.
690
1
                // We made sure when starting that the signature was supported.
691
1
                let has_return_value = Signature::try_from(&function_to_call.ty(&store))
692
1
                    .unwrap()
693
1
                    .return_type()
694
1
                    .is_some();
695
1
696
1
                // Starting the function call.
697
1
                let function_call = Box::pin(async move {
698
                    // Prepare an array of results to pass to `wasmtime`. Note that the type doesn't
699
                    // have to match the actual return value, only the length.
700
                    let mut result = [wasmtime::Val::I32(0)];
701
702
                    let outcome = function_to_call
703
                        .call_async(
704
                            &mut store,
705
                            &params,
706
                            &mut result[..(if has_return_value { 1 } else { 0 })],
707
                        )
708
                        .await;
709
710
                    // Execution resumes here when the Wasm code has finished, gracefully or not.
711
                    match outcome {
712
                        Ok(()) if has_return_value => {
713
                            // TODO: could implement TryFrom on wasmtime::Val instead of &wasmtime::Val to avoid borrow here?
714
                            (store, Ok(Some((&result[0]).try_into().unwrap())))
715
                        }
716
                        Ok(()) => (store, Ok(None)),
717
                        Err(err) => (store, Err(err)),
718
                    }
719
1
                });
720
1
721
1
                self.inner = JitInner::Executing(function_call);
722
            }
723
        };
724
725
        // We made sure that the state is in `Executing`. Now grab the future.
726
4.15k
        let function_call = match &mut self.inner {
727
4.15k
            JitInner::Executing(f) => f,
728
0
            _ => unreachable!(),
729
        };
730
731
        // Resume the coroutine execution.
732
        // The `Future` is polled with a no-op waker. We are in total control of when the
733
        // execution might be able to progress, hence the lack of need for a waker.
734
        match future::Future::poll(
735
4.15k
            function_call.as_mut(),
736
4.15k
            &mut task::Context::from_waker(&noop_waker()),
737
        ) {
738
1
            task::Poll::Ready((store, Ok(val))) => {
739
1
                self.inner = JitInner::Done(store);
740
1
                Ok(ExecOutcome::Finished {
741
1
                    // Since we verify at initialization that the signature of the function to
742
1
                    // call is supported, it is guaranteed that the type of this return value is
743
1
                    // supported too.
744
1
                    return_value: Ok(val),
745
1
                })
746
            }
747
0
            task::Poll::Ready((store, Err(err))) => {
748
0
                self.inner = JitInner::Done(store);
749
0
                Ok(ExecOutcome::Finished {
750
0
                    return_value: Err(Trap(err.to_string())),
751
0
                })
752
            }
753
            task::Poll::Pending => {
754
4.15k
                let mut shared_lock = self.shared.try_lock().unwrap();
755
4.15k
                match mem::replace(&mut *shared_lock, Shared::Poisoned) {
756
                    Shared::EnteredFunctionCall {
757
4.15k
                        function_index,
758
4.15k
                        parameters,
759
4.15k
                        memory,
760
4.15k
                        expected_return_ty,
761
4.15k
                        in_interrupted_waker,
762
4.15k
                    } => {
763
4.15k
                        *shared_lock = Shared::WithinFunctionCall {
764
4.15k
                            memory,
765
4.15k
                            expected_return_ty,
766
4.15k
                            in_interrupted_waker,
767
4.15k
                        };
768
4.15k
769
4.15k
                        Ok(ExecOutcome::Interrupted {
770
4.15k
                            id: function_index,
771
4.15k
                            params: parameters,
772
4.15k
                        })
773
                    }
774
0
                    _ => unreachable!(),
775
                }
776
            }
777
        }
778
4.15k
    }
779
780
    /// See [`super::VirtualMachine::memory_size`].
781
8.76k
    pub fn memory_size(&self) -> HeapPages {
782
8.76k
        match &self.inner {
783
18
            JitInner::NotStarted { 
store4
, .. } | JitInner::Done(store) => {
784
22
                let heap_pages = self.memory.size(store);
785
22
                HeapPages::new(u32::try_from(heap_pages).unwrap())
786
            }
787
            JitInner::Executing(_) => {
788
8.74k
                let size_bytes = match *self.shared.try_lock().unwrap() {
789
8.74k
                    Shared::WithinFunctionCall { memory, .. } => memory.1,
790
0
                    _ => unreachable!(),
791
                };
792
793
8.74k
                if size_bytes == 0 {
794
0
                    HeapPages::new(0)
795
                } else {
796
8.74k
                    HeapPages::new(1 + u32::try_from((size_bytes - 1) / (64 * 1024)).unwrap())
797
                }
798
            }
799
0
            JitInner::Poisoned => unreachable!(),
800
        }
801
8.76k
    }
_RNvMs5_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB5_3Jit11memory_size
Line
Count
Source
781
95
    pub fn memory_size(&self) -> HeapPages {
782
95
        match &self.inner {
783
17
            JitInner::NotStarted { 
store4
, .. } | JitInner::Done(store) => {
784
21
                let heap_pages = self.memory.size(store);
785
21
                HeapPages::new(u32::try_from(heap_pages).unwrap())
786
            }
787
            JitInner::Executing(_) => {
788
74
                let size_bytes = match *self.shared.try_lock().unwrap() {
789
74
                    Shared::WithinFunctionCall { memory, .. } => memory.1,
790
0
                    _ => unreachable!(),
791
                };
792
793
74
                if size_bytes == 0 {
794
0
                    HeapPages::new(0)
795
                } else {
796
74
                    HeapPages::new(1 + u32::try_from((size_bytes - 1) / (64 * 1024)).unwrap())
797
                }
798
            }
799
0
            JitInner::Poisoned => unreachable!(),
800
        }
801
95
    }
_RNvMs5_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB5_3Jit11memory_size
Line
Count
Source
781
8.67k
    pub fn memory_size(&self) -> HeapPages {
782
8.67k
        match &self.inner {
783
1
            JitInner::NotStarted { 
store0
, .. } | JitInner::Done(store) => {
784
1
                let heap_pages = self.memory.size(store);
785
1
                HeapPages::new(u32::try_from(heap_pages).unwrap())
786
            }
787
            JitInner::Executing(_) => {
788
8.67k
                let size_bytes = match *self.shared.try_lock().unwrap() {
789
8.67k
                    Shared::WithinFunctionCall { memory, .. } => memory.1,
790
0
                    _ => unreachable!(),
791
                };
792
793
8.67k
                if size_bytes == 0 {
794
0
                    HeapPages::new(0)
795
                } else {
796
8.67k
                    HeapPages::new(1 + u32::try_from((size_bytes - 1) / (64 * 1024)).unwrap())
797
                }
798
            }
799
0
            JitInner::Poisoned => unreachable!(),
800
        }
801
8.67k
    }
802
803
    /// See [`super::VirtualMachine::read_memory`].
804
2.48k
    pub fn read_memory(
805
2.48k
        &'_ self,
806
2.48k
        offset: u32,
807
2.48k
        size: u32,
808
2.48k
    ) -> Result<impl AsRef<[u8]> + '_, OutOfBoundsError> {
809
2.48k
        let memory_slice = match &self.inner {
810
18
            JitInner::NotStarted { 
store4
, .. } | JitInner::Done(
store14
) => self.memory.data(store),
811
            JitInner::Executing(_) => {
812
2.46k
                let memory = match *self.shared.try_lock().unwrap() {
813
2.46k
                    Shared::WithinFunctionCall { memory, .. } => memory,
814
0
                    _ => unreachable!(),
815
                };
816
817
2.46k
                unsafe { slice::from_raw_parts(memory.0, memory.1) }
818
            }
819
0
            JitInner::Poisoned => unreachable!(),
820
        };
821
822
2.48k
        let start = usize::try_from(offset).map_err(|_| 
OutOfBoundsError0
)
?0
;
Unexecuted instantiation: _RNCNvMs5_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB7_3Jit11read_memory0Bd_
Unexecuted instantiation: _RNCNvMs5_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB7_3Jit11read_memory0Bd_
823
2.48k
        let end = start
824
2.48k
            .checked_add(usize::try_from(size).map_err(|_| 
OutOfBoundsError0
)
?0
)
Unexecuted instantiation: _RNCNvMs5_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB7_3Jit11read_memorys_0Bd_
Unexecuted instantiation: _RNCNvMs5_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB7_3Jit11read_memorys_0Bd_
825
2.48k
            .ok_or(OutOfBoundsError)
?0
;
826
827
2.48k
        if end > memory_slice.len() {
828
0
            return Err(OutOfBoundsError);
829
2.48k
        }
830
2.48k
831
2.48k
        Ok(&memory_slice[start..end])
832
2.48k
    }
_RNvMs5_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB5_3Jit11read_memory
Line
Count
Source
804
38
    pub fn read_memory(
805
38
        &'_ self,
806
38
        offset: u32,
807
38
        size: u32,
808
38
    ) -> Result<impl AsRef<[u8]> + '_, OutOfBoundsError> {
809
38
        let memory_slice = match &self.inner {
810
17
            JitInner::NotStarted { 
store4
, .. } | JitInner::Done(
store13
) => self.memory.data(store),
811
            JitInner::Executing(_) => {
812
21
                let memory = match *self.shared.try_lock().unwrap() {
813
21
                    Shared::WithinFunctionCall { memory, .. } => memory,
814
0
                    _ => unreachable!(),
815
                };
816
817
21
                unsafe { slice::from_raw_parts(memory.0, memory.1) }
818
            }
819
0
            JitInner::Poisoned => unreachable!(),
820
        };
821
822
38
        let start = usize::try_from(offset).map_err(|_| OutOfBoundsError)
?0
;
823
38
        let end = start
824
38
            .checked_add(usize::try_from(size).map_err(|_| OutOfBoundsError)
?0
)
825
38
            .ok_or(OutOfBoundsError)
?0
;
826
827
38
        if end > memory_slice.len() {
828
0
            return Err(OutOfBoundsError);
829
38
        }
830
38
831
38
        Ok(&memory_slice[start..end])
832
38
    }
_RNvMs5_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB5_3Jit11read_memory
Line
Count
Source
804
2.44k
    pub fn read_memory(
805
2.44k
        &'_ self,
806
2.44k
        offset: u32,
807
2.44k
        size: u32,
808
2.44k
    ) -> Result<impl AsRef<[u8]> + '_, OutOfBoundsError> {
809
2.44k
        let memory_slice = match &self.inner {
810
1
            JitInner::NotStarted { 
store0
, .. } | JitInner::Done(store) => self.memory.data(store),
811
            JitInner::Executing(_) => {
812
2.44k
                let memory = match *self.shared.try_lock().unwrap() {
813
2.44k
                    Shared::WithinFunctionCall { memory, .. } => memory,
814
0
                    _ => unreachable!(),
815
                };
816
817
2.44k
                unsafe { slice::from_raw_parts(memory.0, memory.1) }
818
            }
819
0
            JitInner::Poisoned => unreachable!(),
820
        };
821
822
2.44k
        let start = usize::try_from(offset).map_err(|_| OutOfBoundsError)
?0
;
823
2.44k
        let end = start
824
2.44k
            .checked_add(usize::try_from(size).map_err(|_| OutOfBoundsError)
?0
)
825
2.44k
            .ok_or(OutOfBoundsError)
?0
;
826
827
2.44k
        if end > memory_slice.len() {
828
0
            return Err(OutOfBoundsError);
829
2.44k
        }
830
2.44k
831
2.44k
        Ok(&memory_slice[start..end])
832
2.44k
    }
833
834
    /// See [`super::VirtualMachine::write_memory`].
835
4.20k
    pub fn write_memory(&mut self, offset: u32, value: &[u8]) -> Result<(), OutOfBoundsError> {
836
4.20k
        let memory_slice = match &mut self.inner {
837
1
            JitInner::NotStarted { store, .. } | JitInner::Done(
store0
) => {
838
1
                self.memory.data_mut(store)
839
            }
840
            JitInner::Executing(_) => {
841
4.19k
                let memory = match *self.shared.try_lock().unwrap() {
842
4.19k
                    Shared::WithinFunctionCall { memory, .. } => memory,
843
0
                    _ => unreachable!(),
844
                };
845
846
4.19k
                unsafe { slice::from_raw_parts_mut(memory.0, memory.1) }
847
            }
848
0
            JitInner::Poisoned => unreachable!(),
849
        };
850
851
4.20k
        let start = usize::try_from(offset).map_err(|_| 
OutOfBoundsError0
)
?0
;
Unexecuted instantiation: _RNCNvMs5_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB7_3Jit12write_memory0Bd_
Unexecuted instantiation: _RNCNvMs5_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB7_3Jit12write_memory0Bd_
852
4.20k
        let end = start.checked_add(value.len()).ok_or(OutOfBoundsError)
?0
;
853
854
4.20k
        if end > memory_slice.len() {
855
0
            return Err(OutOfBoundsError);
856
4.20k
        }
857
4.20k
858
4.20k
        if !value.is_empty() {
859
4.20k
            memory_slice[start..end].copy_from_slice(value);
860
4.20k
        }
0
861
862
4.20k
        Ok(())
863
4.20k
    }
_RNvMs5_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB5_3Jit12write_memory
Line
Count
Source
835
49
    pub fn write_memory(&mut self, offset: u32, value: &[u8]) -> Result<(), OutOfBoundsError> {
836
49
        let memory_slice = match &mut self.inner {
837
1
            JitInner::NotStarted { store, .. } | JitInner::Done(
store0
) => {
838
1
                self.memory.data_mut(store)
839
            }
840
            JitInner::Executing(_) => {
841
48
                let memory = match *self.shared.try_lock().unwrap() {
842
48
                    Shared::WithinFunctionCall { memory, .. } => memory,
843
0
                    _ => unreachable!(),
844
                };
845
846
48
                unsafe { slice::from_raw_parts_mut(memory.0, memory.1) }
847
            }
848
0
            JitInner::Poisoned => unreachable!(),
849
        };
850
851
49
        let start = usize::try_from(offset).map_err(|_| OutOfBoundsError)
?0
;
852
49
        let end = start.checked_add(value.len()).ok_or(OutOfBoundsError)
?0
;
853
854
49
        if end > memory_slice.len() {
855
0
            return Err(OutOfBoundsError);
856
49
        }
857
49
858
49
        if !value.is_empty() {
859
49
            memory_slice[start..end].copy_from_slice(value);
860
49
        }
0
861
862
49
        Ok(())
863
49
    }
_RNvMs5_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB5_3Jit12write_memory
Line
Count
Source
835
4.15k
    pub fn write_memory(&mut self, offset: u32, value: &[u8]) -> Result<(), OutOfBoundsError> {
836
4.15k
        let memory_slice = match &mut self.inner {
837
0
            JitInner::NotStarted { store, .. } | JitInner::Done(store) => {
838
0
                self.memory.data_mut(store)
839
            }
840
            JitInner::Executing(_) => {
841
4.15k
                let memory = match *self.shared.try_lock().unwrap() {
842
4.15k
                    Shared::WithinFunctionCall { memory, .. } => memory,
843
0
                    _ => unreachable!(),
844
                };
845
846
4.15k
                unsafe { slice::from_raw_parts_mut(memory.0, memory.1) }
847
            }
848
0
            JitInner::Poisoned => unreachable!(),
849
        };
850
851
4.15k
        let start = usize::try_from(offset).map_err(|_| OutOfBoundsError)
?0
;
852
4.15k
        let end = start.checked_add(value.len()).ok_or(OutOfBoundsError)
?0
;
853
854
4.15k
        if end > memory_slice.len() {
855
0
            return Err(OutOfBoundsError);
856
4.15k
        }
857
4.15k
858
4.15k
        if !value.is_empty() {
859
4.15k
            memory_slice[start..end].copy_from_slice(value);
860
4.15k
        }
0
861
862
4.15k
        Ok(())
863
4.15k
    }
864
865
    /// See [`super::VirtualMachine::grow_memory`].
866
9
    pub fn grow_memory(&mut self, additional: HeapPages) -> Result<(), OutOfBoundsError> {
867
9
        let additional = u64::from(u32::from(additional));
868
9
869
9
        match &mut self.inner {
870
2
            JitInner::NotStarted { store, .. } | JitInner::Done(
store0
) => {
871
                // This is the simple case: we still have access to the `store` and can perform
872
                // the growth synchronously.
873
2
                self.memory
874
2
                    .grow(store, additional)
875
2
                    .map_err(|_| 
OutOfBoundsError1
)
?1
;
_RNCNvMs5_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB7_3Jit11grow_memory0Bd_
Line
Count
Source
875
1
                    .map_err(|_| OutOfBoundsError)?;
Unexecuted instantiation: _RNCNvMs5_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB7_3Jit11grow_memory0Bd_
876
            }
877
0
            JitInner::Poisoned => unreachable!(),
878
7
            JitInner::Executing(function_call) => {
879
7
                // This is the complicated case: the call is in progress and we don't have access
880
7
                // to the `store`. Switch `Shared` to `MemoryGrowRequired`, then resume execution
881
7
                // so that the function handler performs the grow.
882
7
                let mut shared_lock = self.shared.try_lock().unwrap();
883
7
                match mem::replace(&mut *shared_lock, Shared::Poisoned) {
884
                    Shared::WithinFunctionCall {
885
7
                        memory,
886
7
                        expected_return_ty,
887
7
                        in_interrupted_waker,
888
                    } => {
889
                        // We check now what the memory bounds are, as it is more difficult to
890
                        // recover from `grow` returning an error than checking manually.
891
7
                        let current_pages = if memory.1 == 0 {
892
0
                            0
893
                        } else {
894
7
                            1 + u64::try_from((memory.1 - 1) / (64 * 1024)).unwrap()
895
                        };
896
7
                        if self
897
7
                            .memory_type
898
7
                            .maximum()
899
7
                            .map_or(false, |max| 
current_pages + additional > max1
)
_RNCNvMs5_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB7_3Jit11grow_memorys_0Bd_
Line
Count
Source
899
1
                            .map_or(false, |max| current_pages + additional > max)
Unexecuted instantiation: _RNCNvMs5_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB7_3Jit11grow_memorys_0Bd_
900
                        {
901
                            // Put everything back as it was.
902
1
                            *shared_lock = Shared::WithinFunctionCall {
903
1
                                memory,
904
1
                                expected_return_ty,
905
1
                                in_interrupted_waker,
906
1
                            };
907
1
                            return Err(OutOfBoundsError);
908
6
                        }
909
910
6
                        if let Some(waker) = in_interrupted_waker {
911
6
                            waker.wake();
912
6
                        }
0
913
914
6
                        *shared_lock = Shared::MemoryGrowRequired {
915
6
                            memory: self.memory,
916
6
                            additional,
917
6
                        }
918
                    }
919
0
                    _ => unreachable!(),
920
                }
921
6
                drop(shared_lock);
922
6
923
6
                // Resume the coroutine execution once for the function handler to pick up the
924
6
                // `MemoryGrowRequired`, perform the grow, and switch back to `WithinFunctionCall`.
925
6
                // The `Future` is polled with a no-op waker. We are in total control of when the
926
6
                // execution might be able to progress, hence the lack of need for a waker.
927
6
                match future::Future::poll(
928
6
                    function_call.as_mut(),
929
6
                    &mut task::Context::from_waker(&noop_waker()),
930
6
                ) {
931
0
                    task::Poll::Ready(_) => unreachable!(),
932
                    task::Poll::Pending => {
933
6
                        debug_assert!(
matches!0
(
934
6
                            *self.shared.try_lock().unwrap(),
935
                            Shared::WithinFunctionCall { .. }
936
                        ));
937
                    }
938
                }
939
            }
940
        }
941
942
7
        Ok(())
943
9
    }
_RNvMs5_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB5_3Jit11grow_memory
Line
Count
Source
866
3
    pub fn grow_memory(&mut self, additional: HeapPages) -> Result<(), OutOfBoundsError> {
867
3
        let additional = u64::from(u32::from(additional));
868
3
869
3
        match &mut self.inner {
870
2
            JitInner::NotStarted { store, .. } | JitInner::Done(
store0
) => {
871
                // This is the simple case: we still have access to the `store` and can perform
872
                // the growth synchronously.
873
2
                self.memory
874
2
                    .grow(store, additional)
875
2
                    .map_err(|_| OutOfBoundsError)
?1
;
876
            }
877
0
            JitInner::Poisoned => unreachable!(),
878
1
            JitInner::Executing(function_call) => {
879
1
                // This is the complicated case: the call is in progress and we don't have access
880
1
                // to the `store`. Switch `Shared` to `MemoryGrowRequired`, then resume execution
881
1
                // so that the function handler performs the grow.
882
1
                let mut shared_lock = self.shared.try_lock().unwrap();
883
1
                match mem::replace(&mut *shared_lock, Shared::Poisoned) {
884
                    Shared::WithinFunctionCall {
885
1
                        memory,
886
1
                        expected_return_ty,
887
1
                        in_interrupted_waker,
888
                    } => {
889
                        // We check now what the memory bounds are, as it is more difficult to
890
                        // recover from `grow` returning an error than checking manually.
891
1
                        let current_pages = if memory.1 == 0 {
892
0
                            0
893
                        } else {
894
1
                            1 + u64::try_from((memory.1 - 1) / (64 * 1024)).unwrap()
895
                        };
896
1
                        if self
897
1
                            .memory_type
898
1
                            .maximum()
899
1
                            .map_or(false, |max| current_pages + additional > max)
900
                        {
901
                            // Put everything back as it was.
902
1
                            *shared_lock = Shared::WithinFunctionCall {
903
1
                                memory,
904
1
                                expected_return_ty,
905
1
                                in_interrupted_waker,
906
1
                            };
907
1
                            return Err(OutOfBoundsError);
908
0
                        }
909
910
0
                        if let Some(waker) = in_interrupted_waker {
911
0
                            waker.wake();
912
0
                        }
913
914
0
                        *shared_lock = Shared::MemoryGrowRequired {
915
0
                            memory: self.memory,
916
0
                            additional,
917
0
                        }
918
                    }
919
0
                    _ => unreachable!(),
920
                }
921
0
                drop(shared_lock);
922
0
923
0
                // Resume the coroutine execution once for the function handler to pick up the
924
0
                // `MemoryGrowRequired`, perform the grow, and switch back to `WithinFunctionCall`.
925
0
                // The `Future` is polled with a no-op waker. We are in total control of when the
926
0
                // execution might be able to progress, hence the lack of need for a waker.
927
0
                match future::Future::poll(
928
0
                    function_call.as_mut(),
929
0
                    &mut task::Context::from_waker(&noop_waker()),
930
0
                ) {
931
0
                    task::Poll::Ready(_) => unreachable!(),
932
                    task::Poll::Pending => {
933
0
                        debug_assert!(matches!(
934
0
                            *self.shared.try_lock().unwrap(),
935
                            Shared::WithinFunctionCall { .. }
936
                        ));
937
                    }
938
                }
939
            }
940
        }
941
942
1
        Ok(())
943
3
    }
_RNvMs5_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB5_3Jit11grow_memory
Line
Count
Source
866
6
    pub fn grow_memory(&mut self, additional: HeapPages) -> Result<(), OutOfBoundsError> {
867
6
        let additional = u64::from(u32::from(additional));
868
6
869
6
        match &mut self.inner {
870
0
            JitInner::NotStarted { store, .. } | JitInner::Done(store) => {
871
                // This is the simple case: we still have access to the `store` and can perform
872
                // the growth synchronously.
873
0
                self.memory
874
0
                    .grow(store, additional)
875
0
                    .map_err(|_| OutOfBoundsError)?;
876
            }
877
0
            JitInner::Poisoned => unreachable!(),
878
6
            JitInner::Executing(function_call) => {
879
6
                // This is the complicated case: the call is in progress and we don't have access
880
6
                // to the `store`. Switch `Shared` to `MemoryGrowRequired`, then resume execution
881
6
                // so that the function handler performs the grow.
882
6
                let mut shared_lock = self.shared.try_lock().unwrap();
883
6
                match mem::replace(&mut *shared_lock, Shared::Poisoned) {
884
                    Shared::WithinFunctionCall {
885
6
                        memory,
886
6
                        expected_return_ty,
887
6
                        in_interrupted_waker,
888
                    } => {
889
                        // We check now what the memory bounds are, as it is more difficult to
890
                        // recover from `grow` returning an error than checking manually.
891
6
                        let current_pages = if memory.1 == 0 {
892
0
                            0
893
                        } else {
894
6
                            1 + u64::try_from((memory.1 - 1) / (64 * 1024)).unwrap()
895
                        };
896
6
                        if self
897
6
                            .memory_type
898
6
                            .maximum()
899
6
                            .map_or(false, |max| current_pages + additional > max)
900
                        {
901
                            // Put everything back as it was.
902
0
                            *shared_lock = Shared::WithinFunctionCall {
903
0
                                memory,
904
0
                                expected_return_ty,
905
0
                                in_interrupted_waker,
906
0
                            };
907
0
                            return Err(OutOfBoundsError);
908
6
                        }
909
910
6
                        if let Some(waker) = in_interrupted_waker {
911
6
                            waker.wake();
912
6
                        }
0
913
914
6
                        *shared_lock = Shared::MemoryGrowRequired {
915
6
                            memory: self.memory,
916
6
                            additional,
917
6
                        }
918
                    }
919
0
                    _ => unreachable!(),
920
                }
921
6
                drop(shared_lock);
922
6
923
6
                // Resume the coroutine execution once for the function handler to pick up the
924
6
                // `MemoryGrowRequired`, perform the grow, and switch back to `WithinFunctionCall`.
925
6
                // The `Future` is polled with a no-op waker. We are in total control of when the
926
6
                // execution might be able to progress, hence the lack of need for a waker.
927
6
                match future::Future::poll(
928
6
                    function_call.as_mut(),
929
6
                    &mut task::Context::from_waker(&noop_waker()),
930
6
                ) {
931
0
                    task::Poll::Ready(_) => unreachable!(),
932
                    task::Poll::Pending => {
933
6
                        debug_assert!(
matches!0
(
934
6
                            *self.shared.try_lock().unwrap(),
935
                            Shared::WithinFunctionCall { .. }
936
                        ));
937
                    }
938
                }
939
            }
940
        }
941
942
6
        Ok(())
943
6
    }
944
945
    /// See [`super::VirtualMachine::into_prototype`].
946
7
    pub fn into_prototype(self) -> JitPrototype {
947
7
        // Since the creation has succeeded before, there's no reason why it would fail now.
948
7
        JitPrototype::from_base_components(self.base_components).unwrap()
949
7
    }
_RNvMs5_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB5_3Jit14into_prototype
Line
Count
Source
946
7
    pub fn into_prototype(self) -> JitPrototype {
947
7
        // Since the creation has succeeded before, there's no reason why it would fail now.
948
7
        JitPrototype::from_base_components(self.base_components).unwrap()
949
7
    }
Unexecuted instantiation: _RNvMs5_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB5_3Jit14into_prototype
950
}
951
952
impl fmt::Debug for Jit {
953
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
954
0
        f.debug_tuple("Jit").finish()
955
0
    }
Unexecuted instantiation: _RNvXs6_NtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jitNtB5_3JitNtNtCsaYZPK01V26L_4core3fmt5Debug3fmt
Unexecuted instantiation: _RNvXs6_NtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jitNtB5_3JitNtNtCsaYZPK01V26L_4core3fmt5Debug3fmt
956
}
957
958
// Because `BoxFuture` doesn't implement `Sync`, `Jit` also doesn't implement `Sync`. In
959
// reality, however, none of the `&self`-accepting functions of `Jit` ever access the
960
// `BoxFuture` and even if they did, there's no `&self`-accepting function on `BoxFuture` itself
961
// anyway.
962
// TODO: maybe find a way to remove this unsafe implementation
963
unsafe impl Sync for Jit {}
964
965
4.30k
fn noop_waker() -> task::Waker {
966
4.30k
    // Safety: all the requirements in the documentation of wakers (e.g. thread safety) is
967
4.30k
    // irrelevant here due to the implementation being trivial.
968
4.30k
    unsafe {
969
4.30k
        fn clone(_: *const ()) -> task::RawWaker {
970
4.20k
            task::RawWaker::new(ptr::null(), &VTABLE)
971
4.20k
        }
_RNvNvNtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jit10noop_waker5clone
Line
Count
Source
969
44
        fn clone(_: *const ()) -> task::RawWaker {
970
44
            task::RawWaker::new(ptr::null(), &VTABLE)
971
44
        }
_RNvNvNtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jit10noop_waker5clone
Line
Count
Source
969
4.15k
        fn clone(_: *const ()) -> task::RawWaker {
970
4.15k
            task::RawWaker::new(ptr::null(), &VTABLE)
971
4.15k
        }
972
8.50k
        fn noop(_: *const ()) {}
_RNvNvNtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jit10noop_waker4noop
Line
Count
Source
972
168
        fn noop(_: *const ()) {}
_RNvNvNtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jit10noop_waker4noop
Line
Count
Source
972
8.34k
        fn noop(_: *const ()) {}
973
4.30k
        static VTABLE: task::RawWakerVTable = task::RawWakerVTable::new(clone, noop, noop, noop);
974
4.30k
        task::Waker::from_raw(task::RawWaker::new(ptr::null(), &VTABLE))
975
4.30k
    }
976
4.30k
}
_RNvNtNtNtCsN16ciHI6Qf_7smoldot8executor2vm3jit10noop_waker
Line
Count
Source
965
124
fn noop_waker() -> task::Waker {
966
124
    // Safety: all the requirements in the documentation of wakers (e.g. thread safety) is
967
124
    // irrelevant here due to the implementation being trivial.
968
124
    unsafe {
969
124
        fn clone(_: *const ()) -> task::RawWaker {
970
124
            task::RawWaker::new(ptr::null(), &VTABLE)
971
124
        }
972
124
        fn noop(_: *const ()) {}
973
124
        static VTABLE: task::RawWakerVTable = task::RawWakerVTable::new(clone, noop, noop, noop);
974
124
        task::Waker::from_raw(task::RawWaker::new(ptr::null(), &VTABLE))
975
124
    }
976
124
}
_RNvNtNtNtCseuYC0Zibziv_7smoldot8executor2vm3jit10noop_waker
Line
Count
Source
965
4.18k
fn noop_waker() -> task::Waker {
966
4.18k
    // Safety: all the requirements in the documentation of wakers (e.g. thread safety) is
967
4.18k
    // irrelevant here due to the implementation being trivial.
968
4.18k
    unsafe {
969
4.18k
        fn clone(_: *const ()) -> task::RawWaker {
970
4.18k
            task::RawWaker::new(ptr::null(), &VTABLE)
971
4.18k
        }
972
4.18k
        fn noop(_: *const ()) {}
973
4.18k
        static VTABLE: task::RawWakerVTable = task::RawWakerVTable::new(clone, noop, noop, noop);
974
4.18k
        task::Waker::from_raw(task::RawWaker::new(ptr::null(), &VTABLE))
975
4.18k
    }
976
4.18k
}