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_config::package::SuggestedCompilerOptimizations;
10use wasmer_types::target::UserCompilerOptimizations as WasmerSuggestedCompilerOptimizations;
11
12use std::{
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::{CompileError, 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::{ModuleCache, ThreadLocalCache},
33 package_loader::{PackageLoader, UnsupportedPackageLoader},
34 resolver::{BackendSource, MultiSource, Source},
35 },
36};
37
38#[derive(Clone)]
39pub enum TaintReason {
40 UnknownWasiVersion,
41 NonZeroExitCode(ExitCode),
42 RuntimeError(RuntimeError),
43 DlSymbolResolutionFailed(String),
44}
45
46#[allow(unused_variables)]
50pub trait Runtime
51where
52 Self: fmt::Debug,
53{
54 fn networking(&self) -> &DynVirtualNetworking;
56
57 fn task_manager(&self) -> &Arc<dyn VirtualTaskManager>;
59
60 fn package_loader(&self) -> Arc<dyn PackageLoader + Send + Sync> {
62 Arc::new(UnsupportedPackageLoader)
63 }
64
65 fn module_cache(&self) -> Arc<dyn ModuleCache + Send + Sync> {
67 Arc::new(ThreadLocalCache::default())
74 }
75
76 fn source(&self) -> Arc<dyn Source + Send + Sync>;
78
79 fn engine(&self) -> Engine {
81 Engine::default()
82 }
83
84 fn engine_with_suggested_opts(
85 &self,
86 suggested_opts: &SuggestedCompilerOptimizations,
87 ) -> Result<Engine, CompileError> {
88 let mut engine = self.engine();
89 engine.with_opts(&WasmerSuggestedCompilerOptimizations {
90 pass_params: suggested_opts.pass_params,
91 })?;
92 Ok(engine)
93 }
94
95 fn new_store(&self) -> wasmer::Store {
97 cfg_if::cfg_if! {
98 if #[cfg(feature = "sys")] {
99 wasmer::Store::new(self.engine())
100 } else {
101 wasmer::Store::default()
102 }
103 }
104 }
105
106 fn http_client(&self) -> Option<&DynHttpClient> {
108 None
109 }
110
111 fn tty(&self) -> Option<&(dyn TtyBridge + Send + Sync)> {
113 None
114 }
115
116 fn load_command_module(
123 &self,
124 cmd: &BinaryPackageCommand,
125 ) -> BoxFuture<'_, Result<Module, SpawnError>> {
126 let module_cache = self.module_cache();
127 let data = HashedModuleData::from_command(cmd);
128
129 let engine = match self.engine_with_suggested_opts(&cmd.suggested_compiler_optimizations) {
130 Ok(engine) => engine,
131 Err(error) => {
132 return Box::pin(async move {
133 Err(SpawnError::CompileError {
134 module_hash: *data.hash(),
135 error,
136 })
137 });
138 }
139 };
140
141 let task = async move { load_module(&engine, &module_cache, &data).await };
142
143 Box::pin(task)
144 }
145
146 fn load_command_module_sync(&self, cmd: &BinaryPackageCommand) -> Result<Module, SpawnError> {
148 block_on(self.load_command_module(cmd))
149 }
150
151 #[deprecated(
155 since = "0.601.0",
156 note = "Use `load_command_module` or `load_hashed_module` instead - this method can have high overhead"
157 )]
158 fn load_module<'a>(&'a self, wasm: &'a [u8]) -> BoxFuture<'a, Result<Module, SpawnError>> {
159 let engine = self.engine();
160 let module_cache = self.module_cache();
161 let data = HashedModuleData::new(wasm.to_vec());
162 let task = async move { load_module(&engine, &module_cache, &data).await };
163 Box::pin(task)
164 }
165
166 #[deprecated(
168 since = "0.601.0",
169 note = "Use `load_command_module` or `load_hashed_module` instead - this method can have high overhead"
170 )]
171 fn load_module_sync(&self, wasm: &[u8]) -> Result<Module, SpawnError> {
172 #[allow(deprecated)]
173 block_on(self.load_module(wasm))
174 }
175
176 fn load_hashed_module(
180 &self,
181 module: HashedModuleData,
182 engine: Option<&Engine>,
183 ) -> BoxFuture<'_, Result<Module, SpawnError>> {
184 let engine = engine.cloned().unwrap_or_else(|| self.engine());
185 let module_cache = self.module_cache();
186 let task = async move { load_module(&engine, &module_cache, &module).await };
187 Box::pin(task)
188 }
189
190 fn load_hashed_module_sync(
192 &self,
193 wasm: HashedModuleData,
194 engine: Option<&Engine>,
195 ) -> Result<Module, SpawnError> {
196 block_on(self.load_hashed_module(wasm, engine))
197 }
198
199 fn on_taint(&self, _reason: TaintReason) {}
202
203 #[cfg(feature = "journal")]
206 fn read_only_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynReadableJournal>> + 'a> {
207 Box::new(std::iter::empty())
208 }
209
210 #[cfg(feature = "journal")]
212 fn writable_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynJournal>> + 'a> {
213 Box::new(std::iter::empty())
214 }
215
216 #[cfg(feature = "journal")]
219 fn active_journal(&self) -> Option<&'_ DynJournal> {
220 None
221 }
222}
223
224pub type DynRuntime = dyn Runtime + Send + Sync;
225
226#[tracing::instrument(level = "debug", skip_all)]
231pub async fn load_module(
232 engine: &Engine,
233 module_cache: &(dyn ModuleCache + Send + Sync),
234 module: &HashedModuleData,
235) -> Result<Module, crate::SpawnError> {
236 let wasm_hash = *module.hash();
237 let result = module_cache.load(wasm_hash, engine).await;
238
239 match result {
240 Ok(module) => return Ok(module),
241 Err(CacheError::NotFound) => {}
242 Err(other) => {
243 tracing::warn!(
244 %wasm_hash,
245 error=&other as &dyn std::error::Error,
246 "Unable to load the cached module",
247 );
248 }
249 }
250
251 let module =
252 Module::new(&engine, module.wasm()).map_err(|err| crate::SpawnError::CompileError {
253 module_hash: wasm_hash,
254 error: err,
255 })?;
256
257 if let Err(e) = module_cache.save(wasm_hash, engine, &module).await {
259 tracing::warn!(
260 %wasm_hash,
261 error=&e as &dyn std::error::Error,
262 "Unable to cache the compiled module",
263 );
264 }
265
266 Ok(module)
267}
268
269#[derive(Debug, Default)]
270pub struct DefaultTty {
271 state: Mutex<WasiTtyState>,
272}
273
274impl TtyBridge for DefaultTty {
275 fn reset(&self) {
276 let mut state = self.state.lock().unwrap();
277 state.echo = false;
278 state.line_buffered = false;
279 state.line_feeds = false
280 }
281
282 fn tty_get(&self) -> WasiTtyState {
283 let state = self.state.lock().unwrap();
284 state.clone()
285 }
286
287 fn tty_set(&self, tty_state: WasiTtyState) {
288 let mut state = self.state.lock().unwrap();
289 *state = tty_state;
290 }
291}
292
293#[derive(Debug, Clone)]
294pub struct PluggableRuntime {
295 pub rt: Arc<dyn VirtualTaskManager>,
296 pub networking: DynVirtualNetworking,
297 pub http_client: Option<DynHttpClient>,
298 pub package_loader: Arc<dyn PackageLoader + Send + Sync>,
299 pub source: Arc<dyn Source + Send + Sync>,
300 pub engine: Engine,
301 pub module_cache: Arc<dyn ModuleCache + Send + Sync>,
302 pub tty: Option<Arc<dyn TtyBridge + Send + Sync>>,
303 #[cfg(feature = "journal")]
304 pub read_only_journals: Vec<Arc<DynReadableJournal>>,
305 #[cfg(feature = "journal")]
306 pub writable_journals: Vec<Arc<DynJournal>>,
307}
308
309impl PluggableRuntime {
310 pub fn new(rt: Arc<dyn VirtualTaskManager>) -> Self {
311 cfg_if::cfg_if! {
313 if #[cfg(feature = "host-vnet")] {
314 let networking = Arc::new(virtual_net::host::LocalNetworking::default());
315 } else {
316 let networking = Arc::new(virtual_net::UnsupportedVirtualNetworking::default());
317 }
318 }
319 let http_client =
320 crate::http::default_http_client().map(|client| Arc::new(client) as DynHttpClient);
321
322 let loader = UnsupportedPackageLoader;
323
324 let mut source = MultiSource::default();
325 if let Some(client) = &http_client {
326 source.add_source(BackendSource::new(
327 BackendSource::WASMER_PROD_ENDPOINT.parse().unwrap(),
328 client.clone(),
329 ));
330 }
331
332 Self {
333 rt,
334 networking,
335 http_client,
336 engine: Default::default(),
337 tty: None,
338 source: Arc::new(source),
339 package_loader: Arc::new(loader),
340 module_cache: Arc::new(module_cache::in_memory()),
341 #[cfg(feature = "journal")]
342 read_only_journals: Vec::new(),
343 #[cfg(feature = "journal")]
344 writable_journals: Vec::new(),
345 }
346 }
347
348 pub fn set_networking_implementation<I>(&mut self, net: I) -> &mut Self
349 where
350 I: VirtualNetworking + Sync,
351 {
352 self.networking = Arc::new(net);
353 self
354 }
355
356 pub fn set_engine(&mut self, engine: Engine) -> &mut Self {
357 self.engine = engine;
358 self
359 }
360
361 pub fn set_tty(&mut self, tty: Arc<dyn TtyBridge + Send + Sync>) -> &mut Self {
362 self.tty = Some(tty);
363 self
364 }
365
366 pub fn set_module_cache(
367 &mut self,
368 module_cache: impl ModuleCache + Send + Sync + 'static,
369 ) -> &mut Self {
370 self.module_cache = Arc::new(module_cache);
371 self
372 }
373
374 pub fn set_source(&mut self, source: impl Source + Send + 'static) -> &mut Self {
375 self.source = Arc::new(source);
376 self
377 }
378
379 pub fn set_package_loader(
380 &mut self,
381 package_loader: impl PackageLoader + 'static,
382 ) -> &mut Self {
383 self.package_loader = Arc::new(package_loader);
384 self
385 }
386
387 pub fn set_http_client(
388 &mut self,
389 client: impl HttpClient + Send + Sync + 'static,
390 ) -> &mut Self {
391 self.http_client = Some(Arc::new(client));
392 self
393 }
394
395 #[cfg(feature = "journal")]
396 pub fn add_read_only_journal(&mut self, journal: Arc<DynReadableJournal>) -> &mut Self {
397 self.read_only_journals.push(journal);
398 self
399 }
400
401 #[cfg(feature = "journal")]
402 pub fn add_writable_journal(&mut self, journal: Arc<DynJournal>) -> &mut Self {
403 self.writable_journals.push(journal);
404 self
405 }
406}
407
408impl Runtime for PluggableRuntime {
409 fn networking(&self) -> &DynVirtualNetworking {
410 &self.networking
411 }
412
413 fn http_client(&self) -> Option<&DynHttpClient> {
414 self.http_client.as_ref()
415 }
416
417 fn package_loader(&self) -> Arc<dyn PackageLoader + Send + Sync> {
418 Arc::clone(&self.package_loader)
419 }
420
421 fn source(&self) -> Arc<dyn Source + Send + Sync> {
422 Arc::clone(&self.source)
423 }
424
425 fn engine(&self) -> Engine {
426 self.engine.clone()
427 }
428
429 fn new_store(&self) -> wasmer::Store {
430 wasmer::Store::new(self.engine.clone())
431 }
432
433 fn task_manager(&self) -> &Arc<dyn VirtualTaskManager> {
434 &self.rt
435 }
436
437 fn tty(&self) -> Option<&(dyn TtyBridge + Send + Sync)> {
438 self.tty.as_deref()
439 }
440
441 fn module_cache(&self) -> Arc<dyn ModuleCache + Send + Sync> {
442 self.module_cache.clone()
443 }
444
445 #[cfg(feature = "journal")]
446 fn read_only_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynReadableJournal>> + 'a> {
447 Box::new(self.read_only_journals.iter().cloned())
448 }
449
450 #[cfg(feature = "journal")]
451 fn writable_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynJournal>> + 'a> {
452 Box::new(self.writable_journals.iter().cloned())
453 }
454
455 #[cfg(feature = "journal")]
456 fn active_journal(&self) -> Option<&DynJournal> {
457 self.writable_journals.iter().last().map(|a| a.as_ref())
458 }
459}
460
461#[derive(Clone, Debug)]
464pub struct OverriddenRuntime {
465 inner: Arc<DynRuntime>,
466 task_manager: Option<Arc<dyn VirtualTaskManager>>,
467 networking: Option<DynVirtualNetworking>,
468 http_client: Option<DynHttpClient>,
469 package_loader: Option<Arc<dyn PackageLoader + Send + Sync>>,
470 source: Option<Arc<dyn Source + Send + Sync>>,
471 engine: Option<Engine>,
472 module_cache: Option<Arc<dyn ModuleCache + Send + Sync>>,
473 tty: Option<Arc<dyn TtyBridge + Send + Sync>>,
474 #[cfg(feature = "journal")]
475 pub read_only_journals: Option<Vec<Arc<DynReadableJournal>>>,
476 #[cfg(feature = "journal")]
477 pub writable_journals: Option<Vec<Arc<DynJournal>>>,
478}
479
480impl OverriddenRuntime {
481 pub fn new(inner: Arc<DynRuntime>) -> Self {
482 Self {
483 inner,
484 task_manager: None,
485 networking: None,
486 http_client: None,
487 package_loader: None,
488 source: None,
489 engine: None,
490 module_cache: None,
491 tty: None,
492 #[cfg(feature = "journal")]
493 read_only_journals: None,
494 #[cfg(feature = "journal")]
495 writable_journals: None,
496 }
497 }
498
499 pub fn with_task_manager(mut self, task_manager: Arc<dyn VirtualTaskManager>) -> Self {
500 self.task_manager.replace(task_manager);
501 self
502 }
503
504 pub fn with_networking(mut self, networking: DynVirtualNetworking) -> Self {
505 self.networking.replace(networking);
506 self
507 }
508
509 pub fn with_http_client(mut self, http_client: DynHttpClient) -> Self {
510 self.http_client.replace(http_client);
511 self
512 }
513
514 pub fn with_package_loader(
515 mut self,
516 package_loader: Arc<dyn PackageLoader + Send + Sync>,
517 ) -> Self {
518 self.package_loader.replace(package_loader);
519 self
520 }
521
522 pub fn with_source(mut self, source: Arc<dyn Source + Send + Sync>) -> Self {
523 self.source.replace(source);
524 self
525 }
526
527 pub fn with_engine(mut self, engine: Engine) -> Self {
528 self.engine.replace(engine);
529 self
530 }
531
532 pub fn with_module_cache(mut self, module_cache: Arc<dyn ModuleCache + Send + Sync>) -> Self {
533 self.module_cache.replace(module_cache);
534 self
535 }
536
537 pub fn with_tty(mut self, tty: Arc<dyn TtyBridge + Send + Sync>) -> Self {
538 self.tty.replace(tty);
539 self
540 }
541
542 #[cfg(feature = "journal")]
543 pub fn with_read_only_journals(mut self, journals: Vec<Arc<DynReadableJournal>>) -> Self {
544 self.read_only_journals.replace(journals);
545 self
546 }
547
548 #[cfg(feature = "journal")]
549 pub fn with_writable_journals(mut self, journals: Vec<Arc<DynJournal>>) -> Self {
550 self.writable_journals.replace(journals);
551 self
552 }
553}
554
555impl Runtime for OverriddenRuntime {
556 fn networking(&self) -> &DynVirtualNetworking {
557 if let Some(net) = self.networking.as_ref() {
558 net
559 } else {
560 self.inner.networking()
561 }
562 }
563
564 fn task_manager(&self) -> &Arc<dyn VirtualTaskManager> {
565 if let Some(rt) = self.task_manager.as_ref() {
566 rt
567 } else {
568 self.inner.task_manager()
569 }
570 }
571
572 fn source(&self) -> Arc<dyn Source + Send + Sync> {
573 if let Some(source) = self.source.clone() {
574 source
575 } else {
576 self.inner.source()
577 }
578 }
579
580 fn package_loader(&self) -> Arc<dyn PackageLoader + Send + Sync> {
581 if let Some(loader) = self.package_loader.clone() {
582 loader
583 } else {
584 self.inner.package_loader()
585 }
586 }
587
588 fn module_cache(&self) -> Arc<dyn ModuleCache + Send + Sync> {
589 if let Some(cache) = self.module_cache.clone() {
590 cache
591 } else {
592 self.inner.module_cache()
593 }
594 }
595
596 fn engine(&self) -> Engine {
597 if let Some(engine) = self.engine.clone() {
598 engine
599 } else {
600 self.inner.engine()
601 }
602 }
603
604 fn new_store(&self) -> wasmer::Store {
605 if let Some(engine) = self.engine.clone() {
606 wasmer::Store::new(engine)
607 } else {
608 self.inner.new_store()
609 }
610 }
611
612 fn http_client(&self) -> Option<&DynHttpClient> {
613 if let Some(client) = self.http_client.as_ref() {
614 Some(client)
615 } else {
616 self.inner.http_client()
617 }
618 }
619
620 fn tty(&self) -> Option<&(dyn TtyBridge + Send + Sync)> {
621 if let Some(tty) = self.tty.as_ref() {
622 Some(tty.deref())
623 } else {
624 self.inner.tty()
625 }
626 }
627
628 #[cfg(feature = "journal")]
629 fn read_only_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynReadableJournal>> + 'a> {
630 if let Some(journals) = self.read_only_journals.as_ref() {
631 Box::new(journals.iter().cloned())
632 } else {
633 self.inner.read_only_journals()
634 }
635 }
636
637 #[cfg(feature = "journal")]
638 fn writable_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynJournal>> + 'a> {
639 if let Some(journals) = self.writable_journals.as_ref() {
640 Box::new(journals.iter().cloned())
641 } else {
642 self.inner.writable_journals()
643 }
644 }
645
646 #[cfg(feature = "journal")]
647 fn active_journal(&self) -> Option<&'_ DynJournal> {
648 if let Some(journals) = self.writable_journals.as_ref() {
649 journals.iter().last().map(|a| a.as_ref())
650 } else {
651 self.inner.active_journal()
652 }
653 }
654
655 fn load_module<'a>(&'a self, data: &[u8]) -> BoxFuture<'a, Result<Module, SpawnError>> {
656 #[allow(deprecated)]
657 if let (Some(engine), Some(module_cache)) = (&self.engine, self.module_cache.clone()) {
658 let engine = engine.clone();
659 let hashed_data = HashedModuleData::new(data.to_vec());
660 let task = async move { load_module(&engine, &module_cache, &hashed_data).await };
661 Box::pin(task)
662 } else {
663 let data = data.to_vec();
664 Box::pin(async move { self.inner.load_module(&data).await })
665 }
666 }
667
668 fn load_module_sync(&self, wasm: &[u8]) -> Result<Module, SpawnError> {
669 #[allow(deprecated)]
670 if self.engine.is_some() || self.module_cache.is_some() {
671 block_on(self.load_module(wasm))
672 } else {
673 self.inner.load_module_sync(wasm)
674 }
675 }
676
677 fn load_hashed_module(
678 &self,
679 data: HashedModuleData,
680 engine: Option<&Engine>,
681 ) -> BoxFuture<'_, Result<Module, SpawnError>> {
682 if self.engine.is_some() || self.module_cache.is_some() {
683 let engine = self.engine();
684 let module_cache = self.module_cache();
685 let task = async move { load_module(&engine, &module_cache, &data).await };
686 Box::pin(task)
687 } else {
688 self.inner.load_hashed_module(data, engine)
689 }
690 }
691
692 fn load_hashed_module_sync(
693 &self,
694 wasm: HashedModuleData,
695 engine: Option<&Engine>,
696 ) -> Result<Module, SpawnError> {
697 if self.engine.is_some() || self.module_cache.is_some() {
698 block_on(self.load_hashed_module(wasm, engine))
699 } else {
700 self.inner.load_hashed_module_sync(wasm, engine)
701 }
702 }
703}