Coverage Report

Created: 2025-09-15 08:51

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