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