wasmer_wasix/runtime/
mod.rs

1pub mod module_cache;
2pub mod package_loader;
3pub mod resolver;
4pub mod task_manager;
5
6use self::module_cache::CacheError;
7pub use self::task_manager::{SpawnType, VirtualTaskManager};
8use module_cache::HashedModuleData;
9use wasmer_types::{CompilationProgressCallback, ModuleHash};
10
11use std::{
12    borrow::Cow,
13    fmt,
14    ops::Deref,
15    sync::{Arc, Mutex},
16};
17
18use futures::future::BoxFuture;
19use virtual_mio::block_on;
20use virtual_net::{DynVirtualNetworking, VirtualNetworking};
21use wasmer::{Engine, Module, RuntimeError};
22use wasmer_wasix_types::wasi::ExitCode;
23
24#[cfg(feature = "journal")]
25use crate::journal::{DynJournal, DynReadableJournal};
26use crate::{
27    SpawnError, WasiTtyState,
28    bin_factory::BinaryPackageCommand,
29    http::{DynHttpClient, HttpClient},
30    os::TtyBridge,
31    runtime::{
32        module_cache::{
33            ModuleCache, ThreadLocalCache,
34            progress::{ModuleLoadProgress, ModuleLoadProgressReporter},
35        },
36        package_loader::{PackageLoader, UnsupportedPackageLoader},
37        resolver::{BackendSource, MultiSource, Source},
38    },
39};
40
41pub type MakeImportCallback = dyn Fn(&wasmer::Module, &mut wasmer::StoreMut) -> anyhow::Result<wasmer::Imports>
42    + Send
43    + Sync
44    + 'static;
45pub type ConfigureInstanceCallback = dyn Fn(
46        &wasmer::Module,
47        &mut wasmer::StoreMut,
48        &wasmer::Instance,
49        Option<&wasmer::Memory>,
50    ) -> anyhow::Result<()>
51    + Send
52    + Sync
53    + 'static;
54
55#[derive(Clone)]
56pub struct ImportCallback(pub Arc<MakeImportCallback>);
57
58impl fmt::Debug for ImportCallback {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        f.write_str("ImportCallback(..)")
61    }
62}
63
64#[derive(Clone)]
65pub struct InstanceCallback(pub Arc<ConfigureInstanceCallback>);
66
67impl fmt::Debug for InstanceCallback {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        f.write_str("InstanceCallback(..)")
70    }
71}
72
73#[derive(Clone)]
74pub enum TaintReason {
75    UnknownWasiVersion,
76    NonZeroExitCode(ExitCode),
77    RuntimeError(RuntimeError),
78    DlSymbolResolutionFailed(String),
79}
80
81/// The input to load a module.
82///
83/// Exists because the semantics for resolving modules can vary between
84/// different sources.
85///
86/// All variants are wrapped in `Cow` to allow for zero-copy usage when possible.
87#[allow(clippy::large_enum_variant)]
88pub enum ModuleInput<'a> {
89    /// Raw bytes.
90    Bytes(Cow<'a, [u8]>),
91    /// Pre-hashed module data.
92    Hashed(Cow<'a, HashedModuleData>),
93    /// A binary package command.
94    Command(Cow<'a, BinaryPackageCommand>),
95}
96
97impl<'a> ModuleInput<'a> {
98    /// Convert to an owned version of the module input.
99    pub fn to_owned(&'a self) -> ModuleInput<'static> {
100        // The manual code below is needed due to compiler issues with the lifetime.
101        match self {
102            Self::Bytes(Cow::Borrowed(b)) => {
103                let v: Vec<u8> = (*b).to_owned();
104                let c: Cow<'static, [u8]> = Cow::from(v);
105                ModuleInput::Bytes(c)
106            }
107            Self::Bytes(Cow::Owned(b)) => ModuleInput::Bytes(Cow::Owned((*b).clone())),
108            Self::Hashed(Cow::Borrowed(h)) => ModuleInput::Hashed(Cow::Owned((*h).clone())),
109            Self::Hashed(Cow::Owned(h)) => ModuleInput::Hashed(Cow::Owned(h.clone())),
110            Self::Command(Cow::Borrowed(c)) => ModuleInput::Command(Cow::Owned((*c).clone())),
111            Self::Command(Cow::Owned(c)) => ModuleInput::Command(Cow::Owned(c.clone())),
112        }
113    }
114
115    /// Get the module hash.
116    ///
117    /// NOTE: may be expensive, depending on the variant.
118    pub fn hash(&self) -> ModuleHash {
119        match self {
120            Self::Bytes(b) => {
121                // Hash on the fly
122                ModuleHash::new(b)
123            }
124            Self::Hashed(hashed) => *hashed.hash(),
125            Self::Command(cmd) => *cmd.hash(),
126        }
127    }
128
129    /// Get the raw WebAssembly bytes.
130    pub fn wasm(&self) -> &[u8] {
131        match self {
132            Self::Bytes(b) => b,
133            Self::Hashed(hashed) => hashed.wasm().as_ref(),
134            Self::Command(cmd) => cmd.atom_ref().as_ref(),
135        }
136    }
137
138    /// Convert to a `HashedModuleData`.
139    ///
140    /// May involve cloning and hashing.
141    pub fn to_hashed(&self) -> HashedModuleData {
142        match self {
143            Self::Bytes(b) => HashedModuleData::new(b.as_ref()),
144            Self::Hashed(hashed) => hashed.as_ref().clone(),
145            Self::Command(cmd) => HashedModuleData::from_command(cmd),
146        }
147    }
148}
149
150/// Runtime components used when running WebAssembly programs.
151///
152/// Think of this as the "System" in "WebAssembly Systems Interface".
153#[allow(unused_variables)]
154pub trait Runtime
155where
156    Self: fmt::Debug,
157{
158    /// Provides access to all the networking related functions such as sockets.
159    fn networking(&self) -> &DynVirtualNetworking;
160
161    /// Retrieve the active [`VirtualTaskManager`].
162    fn task_manager(&self) -> &Arc<dyn VirtualTaskManager>;
163
164    /// A package loader.
165    fn package_loader(&self) -> Arc<dyn PackageLoader + Send + Sync> {
166        Arc::new(UnsupportedPackageLoader)
167    }
168
169    /// A cache for compiled modules.
170    fn module_cache(&self) -> Arc<dyn ModuleCache + Send + Sync> {
171        // Return a cache that uses a thread-local variable. This isn't ideal
172        // because it allows silently sharing state, possibly between runtimes.
173        //
174        // That said, it means people will still get *some* level of caching
175        // because each cache returned by this default implementation will go
176        // through the same thread-local variable.
177        Arc::new(ThreadLocalCache::default())
178    }
179
180    /// The package registry.
181    fn source(&self) -> Arc<dyn Source + Send + Sync>;
182
183    /// Get a [`wasmer::Engine`] for module compilation.
184    fn engine(&self) -> Engine {
185        Engine::default()
186    }
187
188    /// Create a new [`wasmer::Store`].
189    fn new_store(&self) -> wasmer::Store {
190        cfg_if::cfg_if! {
191            if #[cfg(feature = "sys")] {
192                wasmer::Store::new(self.engine())
193            } else {
194                wasmer::Store::default()
195            }
196        }
197    }
198
199    /// Create additional imports for a new WASIX instance in the provided store.
200    ///
201    /// This callback may be invoked multiple times (e.g. process bootstrap,
202    /// thread spawn), so implementations should create imports that are valid
203    /// for the given store each time.
204    fn additional_imports(
205        &self,
206        _module: &wasmer::Module,
207        _store: &mut wasmer::StoreMut,
208    ) -> anyhow::Result<wasmer::Imports> {
209        Ok(wasmer::Imports::new())
210    }
211
212    /// Configure an instantiated instance before initialization/startup.
213    fn configure_new_instance(
214        &self,
215        _module: &wasmer::Module,
216        _store: &mut wasmer::StoreMut,
217        _instance: &wasmer::Instance,
218        _imported_memory: Option<&wasmer::Memory>,
219    ) -> anyhow::Result<()> {
220        Ok(())
221    }
222
223    /// Get a custom HTTP client
224    fn http_client(&self) -> Option<&DynHttpClient> {
225        None
226    }
227
228    /// Get access to the TTY used by the environment.
229    fn tty(&self) -> Option<&(dyn TtyBridge + Send + Sync)> {
230        None
231    }
232
233    /// The primary way to load a module given a module input.
234    ///
235    /// The engine to use can be optionally provided, otherwise the most appropriate engine
236    /// should be selected.
237    ///
238    /// An optional progress reporter callback can be provided to report progress during module loading.
239    fn resolve_module<'a>(
240        &'a self,
241        input: ModuleInput<'a>,
242        engine: Option<&Engine>,
243        on_progress: Option<ModuleLoadProgressReporter>,
244    ) -> BoxFuture<'a, Result<Module, SpawnError>> {
245        let data = input.to_hashed();
246
247        let engine = if let Some(e) = engine {
248            e.clone()
249        } else {
250            match &input {
251                ModuleInput::Bytes(_) => self.engine(),
252                ModuleInput::Hashed(_) => self.engine(),
253                ModuleInput::Command(cmd) => self.engine(),
254            }
255        };
256
257        let module_cache = self.module_cache();
258
259        let task = async move { load_module(&engine, &module_cache, input, on_progress).await };
260        Box::pin(task)
261    }
262
263    /// Sync variant of [`Self::resolve_module`].
264    fn resolve_module_sync(
265        &self,
266        input: ModuleInput<'_>,
267        engine: Option<&Engine>,
268        on_progress: Option<ModuleLoadProgressReporter>,
269    ) -> Result<Module, SpawnError> {
270        block_on(self.resolve_module(input, engine, on_progress))
271    }
272
273    /// Load the module for a command.
274    ///
275    /// Will load the module from the cache if possible, otherwise will compile.
276    ///
277    /// NOTE: This always be preferred over [`Self::load_module`] to avoid
278    /// re-hashing the module!
279    #[deprecated(since = "0.601.0", note = "Use `resolve_module` instead")]
280    fn load_command_module(
281        &self,
282        cmd: &BinaryPackageCommand,
283    ) -> BoxFuture<'_, Result<Module, SpawnError>> {
284        self.resolve_module(ModuleInput::Command(Cow::Owned(cmd.clone())), None, None)
285    }
286
287    /// Sync version of [`Self::load_command_module`].
288    #[deprecated(since = "0.601.0", note = "Use `resolve_module_sync` instead")]
289    fn load_command_module_sync(&self, cmd: &BinaryPackageCommand) -> Result<Module, SpawnError> {
290        block_on(self.resolve_module(ModuleInput::Command(Cow::Borrowed(cmd)), None, None))
291    }
292
293    /// Load a WebAssembly module from raw bytes.
294    ///
295    /// Will load the module from the cache if possible, otherwise will compile.
296    #[deprecated(since = "0.601.0", note = "Use `resolve_module` instead")]
297    fn load_module<'a>(&'a self, wasm: &'a [u8]) -> BoxFuture<'a, Result<Module, SpawnError>> {
298        self.resolve_module(ModuleInput::Bytes(Cow::Borrowed(wasm)), None, None)
299    }
300
301    /// Synchronous version of [`Self::load_module`].
302    #[deprecated(
303        since = "0.601.0",
304        note = "Use `load_command_module` or `load_hashed_module` instead - this method can have high overhead"
305    )]
306    fn load_module_sync(&self, wasm: &[u8]) -> Result<Module, SpawnError> {
307        block_on(self.resolve_module(ModuleInput::Bytes(Cow::Borrowed(wasm)), None, None))
308    }
309
310    /// Load a WebAssembly module from pre-hashed data.
311    ///
312    /// Will load the module from the cache if possible, otherwise will compile.
313    fn load_hashed_module(
314        &self,
315        module: HashedModuleData,
316        engine: Option<&Engine>,
317    ) -> BoxFuture<'_, Result<Module, SpawnError>> {
318        self.resolve_module(ModuleInput::Hashed(Cow::Owned(module)), engine, None)
319    }
320
321    /// Synchronous version of [`Self::load_hashed_module`].
322    fn load_hashed_module_sync(
323        &self,
324        wasm: HashedModuleData,
325        engine: Option<&Engine>,
326    ) -> Result<Module, SpawnError> {
327        block_on(self.resolve_module(ModuleInput::Hashed(Cow::Owned(wasm)), engine, None))
328    }
329
330    /// Callback thats invokes whenever the instance is tainted, tainting can occur
331    /// for multiple reasons however the most common is a panic within the process
332    fn on_taint(&self, _reason: TaintReason) {}
333
334    /// The list of all read-only journals which will be used to restore the state of the
335    /// runtime at a particular point in time
336    #[cfg(feature = "journal")]
337    fn read_only_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynReadableJournal>> + 'a> {
338        Box::new(std::iter::empty())
339    }
340
341    /// The list of writable journals which will be appended to
342    #[cfg(feature = "journal")]
343    fn writable_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynJournal>> + 'a> {
344        Box::new(std::iter::empty())
345    }
346
347    /// The snapshot capturer takes and restores snapshots of the WASM process at specific
348    /// points in time by reading and writing log entries
349    #[cfg(feature = "journal")]
350    fn active_journal(&self) -> Option<&'_ DynJournal> {
351        None
352    }
353}
354
355pub type DynRuntime = dyn Runtime + Send + Sync;
356
357/// Load a a Webassembly module, trying to use a pre-compiled version if possible.
358///
359// This function exists to provide a reusable baseline implementation for
360// implementing [`Runtime::load_module`], so custom logic can be added on top.
361#[tracing::instrument(level = "debug", skip_all)]
362pub async fn load_module(
363    engine: &Engine,
364    module_cache: &(dyn ModuleCache + Send + Sync),
365    input: ModuleInput<'_>,
366    on_progress: Option<ModuleLoadProgressReporter>,
367) -> Result<Module, crate::SpawnError> {
368    let wasm_hash = input.hash();
369
370    let result = if let Some(on_progress) = &on_progress {
371        module_cache
372            .load_with_progress(wasm_hash, engine, on_progress.clone())
373            .await
374    } else {
375        module_cache.load(wasm_hash, engine).await
376    };
377
378    match result {
379        Ok(module) => return Ok(module),
380        Err(CacheError::NotFound) => {}
381        Err(other) => {
382            tracing::warn!(
383                %wasm_hash,
384                error=&other as &dyn std::error::Error,
385                "Unable to load the cached module",
386            );
387        }
388    }
389
390    let res = if let Some(progress) = on_progress {
391        #[allow(unused_variables)]
392        let p = CompilationProgressCallback::new(move |p| {
393            progress.notify(ModuleLoadProgress::CompilingModule(p))
394        });
395        #[cfg(feature = "sys-default")]
396        {
397            if engine.is_sys() {
398                use wasmer::sys::NativeEngineExt;
399                engine.new_module_with_progress(input.wasm(), p)
400            } else {
401                Module::new(&engine, input.wasm())
402            }
403        }
404        #[cfg(not(feature = "sys-default"))]
405        {
406            Module::new(&engine, input.wasm())
407        }
408    } else {
409        Module::new(&engine, input.wasm())
410    };
411
412    let module = res.map_err(|err| crate::SpawnError::CompileError {
413        module_hash: wasm_hash,
414        error: err,
415    })?;
416
417    // TODO: pass a [`HashedModule`] struct that is safe by construction.
418    if let Err(e) = module_cache.save(wasm_hash, engine, &module).await {
419        tracing::warn!(
420            %wasm_hash,
421            error=&e as &dyn std::error::Error,
422            "Unable to cache the compiled module",
423        );
424    }
425
426    Ok(module)
427}
428
429#[derive(Debug, Default)]
430pub struct DefaultTty {
431    state: Mutex<WasiTtyState>,
432}
433
434impl TtyBridge for DefaultTty {
435    fn reset(&self) {
436        let mut state = self.state.lock().unwrap();
437        state.echo = false;
438        state.line_buffered = false;
439        state.line_feeds = false
440    }
441
442    fn tty_get(&self) -> WasiTtyState {
443        let state = self.state.lock().unwrap();
444        state.clone()
445    }
446
447    fn tty_set(&self, tty_state: WasiTtyState) {
448        let mut state = self.state.lock().unwrap();
449        *state = tty_state;
450    }
451}
452
453#[derive(Debug, Clone)]
454pub struct PluggableRuntime {
455    pub rt: Arc<dyn VirtualTaskManager>,
456    pub networking: DynVirtualNetworking,
457    pub http_client: Option<DynHttpClient>,
458    pub package_loader: Arc<dyn PackageLoader + Send + Sync>,
459    pub source: Arc<dyn Source + Send + Sync>,
460    pub engine: Engine,
461    pub module_cache: Arc<dyn ModuleCache + Send + Sync>,
462    pub tty: Option<Arc<dyn TtyBridge + Send + Sync>>,
463    #[cfg(feature = "journal")]
464    pub read_only_journals: Vec<Arc<DynReadableJournal>>,
465    #[cfg(feature = "journal")]
466    pub writable_journals: Vec<Arc<DynJournal>>,
467    pub additional_imports: Vec<ImportCallback>,
468    pub instance_callbacks: Vec<InstanceCallback>,
469}
470
471impl PluggableRuntime {
472    pub fn new(rt: Arc<dyn VirtualTaskManager>) -> Self {
473        // TODO: the cfg flags below should instead be handled by separate implementations.
474        cfg_if::cfg_if! {
475            if #[cfg(feature = "host-vnet")] {
476                let networking = Arc::new(virtual_net::host::LocalNetworking::default());
477            } else {
478                let networking = Arc::new(virtual_net::UnsupportedVirtualNetworking::default());
479            }
480        }
481        let http_client =
482            crate::http::default_http_client().map(|client| Arc::new(client) as DynHttpClient);
483
484        let loader = UnsupportedPackageLoader;
485
486        let mut source = MultiSource::default();
487        if let Some(client) = &http_client {
488            source.add_source(BackendSource::new(
489                BackendSource::WASMER_PROD_ENDPOINT.parse().unwrap(),
490                client.clone(),
491            ));
492        }
493
494        Self {
495            rt,
496            networking,
497            http_client,
498            engine: Default::default(),
499            tty: None,
500            source: Arc::new(source),
501            package_loader: Arc::new(loader),
502            module_cache: Arc::new(module_cache::in_memory()),
503            #[cfg(feature = "journal")]
504            read_only_journals: Vec::new(),
505            #[cfg(feature = "journal")]
506            writable_journals: Vec::new(),
507            additional_imports: Vec::new(),
508            instance_callbacks: Vec::new(),
509        }
510    }
511
512    pub fn set_networking_implementation<I>(&mut self, net: I) -> &mut Self
513    where
514        I: VirtualNetworking + Sync,
515    {
516        self.networking = Arc::new(net);
517        self
518    }
519
520    pub fn set_engine(&mut self, engine: Engine) -> &mut Self {
521        self.engine = engine;
522        self
523    }
524
525    pub fn set_tty(&mut self, tty: Arc<dyn TtyBridge + Send + Sync>) -> &mut Self {
526        self.tty = Some(tty);
527        self
528    }
529
530    pub fn set_module_cache(
531        &mut self,
532        module_cache: impl ModuleCache + Send + Sync + 'static,
533    ) -> &mut Self {
534        self.module_cache = Arc::new(module_cache);
535        self
536    }
537
538    pub fn set_source(&mut self, source: impl Source + Send + 'static) -> &mut Self {
539        self.source = Arc::new(source);
540        self
541    }
542
543    pub fn set_package_loader(
544        &mut self,
545        package_loader: impl PackageLoader + 'static,
546    ) -> &mut Self {
547        self.package_loader = Arc::new(package_loader);
548        self
549    }
550
551    pub fn set_http_client(
552        &mut self,
553        client: impl HttpClient + Send + Sync + 'static,
554    ) -> &mut Self {
555        self.http_client = Some(Arc::new(client));
556        self
557    }
558
559    #[cfg(feature = "journal")]
560    pub fn add_read_only_journal(&mut self, journal: Arc<DynReadableJournal>) -> &mut Self {
561        self.read_only_journals.push(journal);
562        self
563    }
564
565    #[cfg(feature = "journal")]
566    pub fn add_writable_journal(&mut self, journal: Arc<DynJournal>) -> &mut Self {
567        self.writable_journals.push(journal);
568        self
569    }
570
571    pub fn with_additional_imports(
572        &mut self,
573        imports: impl Fn(&wasmer::Module, &mut wasmer::StoreMut) -> anyhow::Result<wasmer::Imports>
574        + Send
575        + Sync
576        + 'static,
577    ) -> &mut Self {
578        self.additional_imports
579            .push(ImportCallback(Arc::new(imports)));
580        self
581    }
582
583    pub fn with_instance_setup(
584        &mut self,
585        callback: impl Fn(
586            &wasmer::Module,
587            &mut wasmer::StoreMut,
588            &wasmer::Instance,
589            Option<&wasmer::Memory>,
590        ) -> anyhow::Result<()>
591        + Send
592        + Sync
593        + 'static,
594    ) -> &mut Self {
595        self.instance_callbacks
596            .push(InstanceCallback(Arc::new(callback)));
597        self
598    }
599}
600
601impl Runtime for PluggableRuntime {
602    fn networking(&self) -> &DynVirtualNetworking {
603        &self.networking
604    }
605
606    fn http_client(&self) -> Option<&DynHttpClient> {
607        self.http_client.as_ref()
608    }
609
610    fn package_loader(&self) -> Arc<dyn PackageLoader + Send + Sync> {
611        Arc::clone(&self.package_loader)
612    }
613
614    fn source(&self) -> Arc<dyn Source + Send + Sync> {
615        Arc::clone(&self.source)
616    }
617
618    fn engine(&self) -> Engine {
619        self.engine.clone()
620    }
621
622    fn new_store(&self) -> wasmer::Store {
623        wasmer::Store::new(self.engine.clone())
624    }
625
626    fn task_manager(&self) -> &Arc<dyn VirtualTaskManager> {
627        &self.rt
628    }
629
630    fn tty(&self) -> Option<&(dyn TtyBridge + Send + Sync)> {
631        self.tty.as_deref()
632    }
633
634    fn module_cache(&self) -> Arc<dyn ModuleCache + Send + Sync> {
635        self.module_cache.clone()
636    }
637
638    fn additional_imports(
639        &self,
640        module: &wasmer::Module,
641        store: &mut wasmer::StoreMut,
642    ) -> anyhow::Result<wasmer::Imports> {
643        let mut imports = wasmer::Imports::new();
644        for cb in &self.additional_imports {
645            imports.extend(&(*(cb.0))(module, store)?);
646        }
647        Ok(imports)
648    }
649
650    fn configure_new_instance(
651        &self,
652        module: &wasmer::Module,
653        store: &mut wasmer::StoreMut,
654        instance: &wasmer::Instance,
655        imported_memory: Option<&wasmer::Memory>,
656    ) -> anyhow::Result<()> {
657        for cb in &self.instance_callbacks {
658            (*(cb.0))(module, store, instance, imported_memory)?;
659        }
660        Ok(())
661    }
662
663    #[cfg(feature = "journal")]
664    fn read_only_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynReadableJournal>> + 'a> {
665        Box::new(self.read_only_journals.iter().cloned())
666    }
667
668    #[cfg(feature = "journal")]
669    fn writable_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynJournal>> + 'a> {
670        Box::new(self.writable_journals.iter().cloned())
671    }
672
673    #[cfg(feature = "journal")]
674    fn active_journal(&self) -> Option<&DynJournal> {
675        self.writable_journals.iter().last().map(|a| a.as_ref())
676    }
677}
678
679/// Runtime that allows for certain things to be overridden
680/// such as the active journals
681#[derive(Clone, Debug)]
682pub struct OverriddenRuntime {
683    inner: Arc<DynRuntime>,
684    task_manager: Option<Arc<dyn VirtualTaskManager>>,
685    networking: Option<DynVirtualNetworking>,
686    http_client: Option<DynHttpClient>,
687    package_loader: Option<Arc<dyn PackageLoader + Send + Sync>>,
688    source: Option<Arc<dyn Source + Send + Sync>>,
689    engine: Option<Engine>,
690    module_cache: Option<Arc<dyn ModuleCache + Send + Sync>>,
691    tty: Option<Arc<dyn TtyBridge + Send + Sync>>,
692    additional_imports: Vec<ImportCallback>,
693    instance_callbacks: Vec<InstanceCallback>,
694    #[cfg(feature = "journal")]
695    pub read_only_journals: Option<Vec<Arc<DynReadableJournal>>>,
696    #[cfg(feature = "journal")]
697    pub writable_journals: Option<Vec<Arc<DynJournal>>>,
698}
699
700impl OverriddenRuntime {
701    pub fn new(inner: Arc<DynRuntime>) -> Self {
702        Self {
703            inner,
704            task_manager: None,
705            networking: None,
706            http_client: None,
707            package_loader: None,
708            source: None,
709            engine: None,
710            module_cache: None,
711            tty: None,
712            additional_imports: Vec::new(),
713            instance_callbacks: Vec::new(),
714            #[cfg(feature = "journal")]
715            read_only_journals: None,
716            #[cfg(feature = "journal")]
717            writable_journals: None,
718        }
719    }
720
721    pub fn with_task_manager(mut self, task_manager: Arc<dyn VirtualTaskManager>) -> Self {
722        self.task_manager.replace(task_manager);
723        self
724    }
725
726    pub fn with_networking(mut self, networking: DynVirtualNetworking) -> Self {
727        self.networking.replace(networking);
728        self
729    }
730
731    pub fn with_http_client(mut self, http_client: DynHttpClient) -> Self {
732        self.http_client.replace(http_client);
733        self
734    }
735
736    pub fn with_package_loader(
737        mut self,
738        package_loader: Arc<dyn PackageLoader + Send + Sync>,
739    ) -> Self {
740        self.package_loader.replace(package_loader);
741        self
742    }
743
744    pub fn with_source(mut self, source: Arc<dyn Source + Send + Sync>) -> Self {
745        self.source.replace(source);
746        self
747    }
748
749    pub fn with_engine(mut self, engine: Engine) -> Self {
750        self.engine.replace(engine);
751        self
752    }
753
754    pub fn with_module_cache(mut self, module_cache: Arc<dyn ModuleCache + Send + Sync>) -> Self {
755        self.module_cache.replace(module_cache);
756        self
757    }
758
759    pub fn with_tty(mut self, tty: Arc<dyn TtyBridge + Send + Sync>) -> Self {
760        self.tty.replace(tty);
761        self
762    }
763
764    pub fn with_additional_imports(
765        mut self,
766        imports: impl Fn(&wasmer::Module, &mut wasmer::StoreMut) -> anyhow::Result<wasmer::Imports>
767        + Send
768        + Sync
769        + 'static,
770    ) -> Self {
771        self.additional_imports
772            .push(ImportCallback(Arc::new(imports)));
773        self
774    }
775
776    pub fn with_instance_setup(
777        mut self,
778        callback: impl Fn(
779            &wasmer::Module,
780            &mut wasmer::StoreMut,
781            &wasmer::Instance,
782            Option<&wasmer::Memory>,
783        ) -> anyhow::Result<()>
784        + Send
785        + Sync
786        + 'static,
787    ) -> Self {
788        self.instance_callbacks
789            .push(InstanceCallback(Arc::new(callback)));
790        self
791    }
792
793    #[cfg(feature = "journal")]
794    pub fn with_read_only_journals(mut self, journals: Vec<Arc<DynReadableJournal>>) -> Self {
795        self.read_only_journals.replace(journals);
796        self
797    }
798
799    #[cfg(feature = "journal")]
800    pub fn with_writable_journals(mut self, journals: Vec<Arc<DynJournal>>) -> Self {
801        self.writable_journals.replace(journals);
802        self
803    }
804}
805
806impl Runtime for OverriddenRuntime {
807    fn networking(&self) -> &DynVirtualNetworking {
808        if let Some(net) = self.networking.as_ref() {
809            net
810        } else {
811            self.inner.networking()
812        }
813    }
814
815    fn task_manager(&self) -> &Arc<dyn VirtualTaskManager> {
816        if let Some(rt) = self.task_manager.as_ref() {
817            rt
818        } else {
819            self.inner.task_manager()
820        }
821    }
822
823    fn source(&self) -> Arc<dyn Source + Send + Sync> {
824        if let Some(source) = self.source.clone() {
825            source
826        } else {
827            self.inner.source()
828        }
829    }
830
831    fn package_loader(&self) -> Arc<dyn PackageLoader + Send + Sync> {
832        if let Some(loader) = self.package_loader.clone() {
833            loader
834        } else {
835            self.inner.package_loader()
836        }
837    }
838
839    fn module_cache(&self) -> Arc<dyn ModuleCache + Send + Sync> {
840        if let Some(cache) = self.module_cache.clone() {
841            cache
842        } else {
843            self.inner.module_cache()
844        }
845    }
846
847    fn engine(&self) -> Engine {
848        if let Some(engine) = self.engine.clone() {
849            engine
850        } else {
851            self.inner.engine()
852        }
853    }
854
855    fn new_store(&self) -> wasmer::Store {
856        if let Some(engine) = self.engine.clone() {
857            wasmer::Store::new(engine)
858        } else {
859            self.inner.new_store()
860        }
861    }
862
863    fn additional_imports(
864        &self,
865        module: &wasmer::Module,
866        store: &mut wasmer::StoreMut,
867    ) -> anyhow::Result<wasmer::Imports> {
868        let mut imports = self.inner.additional_imports(module, store)?;
869        for cb in &self.additional_imports {
870            imports.extend(&(*(cb.0))(module, store)?);
871        }
872        Ok(imports)
873    }
874
875    fn configure_new_instance(
876        &self,
877        module: &wasmer::Module,
878        store: &mut wasmer::StoreMut,
879        instance: &wasmer::Instance,
880        imported_memory: Option<&wasmer::Memory>,
881    ) -> anyhow::Result<()> {
882        self.inner
883            .configure_new_instance(module, store, instance, imported_memory)?;
884        for cb in &self.instance_callbacks {
885            (*(cb.0))(module, store, instance, imported_memory)?;
886        }
887        Ok(())
888    }
889
890    fn http_client(&self) -> Option<&DynHttpClient> {
891        if let Some(client) = self.http_client.as_ref() {
892            Some(client)
893        } else {
894            self.inner.http_client()
895        }
896    }
897
898    fn tty(&self) -> Option<&(dyn TtyBridge + Send + Sync)> {
899        if let Some(tty) = self.tty.as_ref() {
900            Some(tty.deref())
901        } else {
902            self.inner.tty()
903        }
904    }
905
906    #[cfg(feature = "journal")]
907    fn read_only_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynReadableJournal>> + 'a> {
908        if let Some(journals) = self.read_only_journals.as_ref() {
909            Box::new(journals.iter().cloned())
910        } else {
911            self.inner.read_only_journals()
912        }
913    }
914
915    #[cfg(feature = "journal")]
916    fn writable_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynJournal>> + 'a> {
917        if let Some(journals) = self.writable_journals.as_ref() {
918            Box::new(journals.iter().cloned())
919        } else {
920            self.inner.writable_journals()
921        }
922    }
923
924    #[cfg(feature = "journal")]
925    fn active_journal(&self) -> Option<&'_ DynJournal> {
926        if let Some(journals) = self.writable_journals.as_ref() {
927            journals.iter().last().map(|a| a.as_ref())
928        } else {
929            self.inner.active_journal()
930        }
931    }
932}