Coverage Report

Created: 2025-07-01 09:16

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