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::{
11 CompilationProgressCallback, ModuleHash,
12 target::UserCompilerOptimizations as WasmerSuggestedCompilerOptimizations,
13};
14
15use std::{
16 borrow::Cow,
17 fmt,
18 ops::Deref,
19 sync::{Arc, Mutex},
20};
21
22use futures::future::BoxFuture;
23use virtual_mio::block_on;
24use virtual_net::{DynVirtualNetworking, VirtualNetworking};
25use wasmer::{CompileError, Engine, Module, RuntimeError};
26use wasmer_wasix_types::wasi::ExitCode;
27
28#[cfg(feature = "journal")]
29use crate::journal::{DynJournal, DynReadableJournal};
30use crate::{
31 SpawnError, WasiTtyState,
32 bin_factory::BinaryPackageCommand,
33 http::{DynHttpClient, HttpClient},
34 os::TtyBridge,
35 runtime::{
36 module_cache::{
37 ModuleCache, ThreadLocalCache,
38 progress::{ModuleLoadProgress, ModuleLoadProgressReporter},
39 },
40 package_loader::{PackageLoader, UnsupportedPackageLoader},
41 resolver::{BackendSource, MultiSource, Source},
42 },
43};
44
45#[derive(Clone)]
46pub enum TaintReason {
47 UnknownWasiVersion,
48 NonZeroExitCode(ExitCode),
49 RuntimeError(RuntimeError),
50 DlSymbolResolutionFailed(String),
51}
52
53pub enum ModuleInput<'a> {
60 Bytes(Cow<'a, [u8]>),
62 Hashed(Cow<'a, HashedModuleData>),
64 Command(Cow<'a, BinaryPackageCommand>),
66}
67
68impl<'a> ModuleInput<'a> {
69 pub fn to_owned(&'a self) -> ModuleInput<'static> {
71 match self {
73 Self::Bytes(Cow::Borrowed(b)) => {
74 let v: Vec<u8> = (*b).to_owned();
75 let c: Cow<'static, [u8]> = Cow::from(v);
76 ModuleInput::Bytes(c)
77 }
78 Self::Bytes(Cow::Owned(b)) => ModuleInput::Bytes(Cow::Owned((*b).clone())),
79 Self::Hashed(Cow::Borrowed(h)) => ModuleInput::Hashed(Cow::Owned((*h).clone())),
80 Self::Hashed(Cow::Owned(h)) => ModuleInput::Hashed(Cow::Owned(h.clone())),
81 Self::Command(Cow::Borrowed(c)) => ModuleInput::Command(Cow::Owned((*c).clone())),
82 Self::Command(Cow::Owned(c)) => ModuleInput::Command(Cow::Owned(c.clone())),
83 }
84 }
85
86 pub fn hash(&self) -> ModuleHash {
90 match self {
91 Self::Bytes(b) => {
92 ModuleHash::new(b)
94 }
95 Self::Hashed(hashed) => *hashed.hash(),
96 Self::Command(cmd) => *cmd.hash(),
97 }
98 }
99
100 pub fn wasm(&self) -> &[u8] {
102 match self {
103 Self::Bytes(b) => b,
104 Self::Hashed(hashed) => hashed.wasm().as_ref(),
105 Self::Command(cmd) => cmd.atom_ref().as_ref(),
106 }
107 }
108
109 pub fn to_hashed(&self) -> HashedModuleData {
113 match self {
114 Self::Bytes(b) => HashedModuleData::new(b.as_ref()),
115 Self::Hashed(hashed) => hashed.as_ref().clone(),
116 Self::Command(cmd) => HashedModuleData::from_command(cmd),
117 }
118 }
119}
120
121#[allow(unused_variables)]
125pub trait Runtime
126where
127 Self: fmt::Debug,
128{
129 fn networking(&self) -> &DynVirtualNetworking;
131
132 fn task_manager(&self) -> &Arc<dyn VirtualTaskManager>;
134
135 fn package_loader(&self) -> Arc<dyn PackageLoader + Send + Sync> {
137 Arc::new(UnsupportedPackageLoader)
138 }
139
140 fn module_cache(&self) -> Arc<dyn ModuleCache + Send + Sync> {
142 Arc::new(ThreadLocalCache::default())
149 }
150
151 fn source(&self) -> Arc<dyn Source + Send + Sync>;
153
154 fn engine(&self) -> Engine {
156 Engine::default()
157 }
158
159 fn engine_with_suggested_opts(
160 &self,
161 suggested_opts: &SuggestedCompilerOptimizations,
162 ) -> Result<Engine, CompileError> {
163 let mut engine = self.engine();
164 engine.with_opts(&WasmerSuggestedCompilerOptimizations {
165 pass_params: suggested_opts.pass_params,
166 })?;
167 Ok(engine)
168 }
169
170 fn new_store(&self) -> wasmer::Store {
172 cfg_if::cfg_if! {
173 if #[cfg(feature = "sys")] {
174 wasmer::Store::new(self.engine())
175 } else {
176 wasmer::Store::default()
177 }
178 }
179 }
180
181 fn http_client(&self) -> Option<&DynHttpClient> {
183 None
184 }
185
186 fn tty(&self) -> Option<&(dyn TtyBridge + Send + Sync)> {
188 None
189 }
190
191 fn resolve_module<'a>(
198 &'a self,
199 input: ModuleInput<'a>,
200 engine: Option<&Engine>,
201 on_progress: Option<ModuleLoadProgressReporter>,
202 ) -> BoxFuture<'a, Result<Module, SpawnError>> {
203 let data = input.to_hashed();
204
205 let engine = if let Some(e) = engine {
206 e.clone()
207 } else {
208 match &input {
209 ModuleInput::Bytes(_) => self.engine(),
210 ModuleInput::Hashed(_) => self.engine(),
211 ModuleInput::Command(cmd) => {
212 match self
213 .engine_with_suggested_opts(&cmd.as_ref().suggested_compiler_optimizations)
214 {
215 Ok(engine) => engine,
216 Err(error) => {
217 return Box::pin(async move {
218 Err(SpawnError::CompileError {
219 module_hash: *data.hash(),
220 error,
221 })
222 });
223 }
224 }
225 }
226 }
227 };
228
229 let module_cache = self.module_cache();
230
231 let task = async move { load_module(&engine, &module_cache, input, on_progress).await };
232 Box::pin(task)
233 }
234
235 fn resolve_module_sync(
237 &self,
238 input: ModuleInput<'_>,
239 engine: Option<&Engine>,
240 on_progress: Option<ModuleLoadProgressReporter>,
241 ) -> Result<Module, SpawnError> {
242 block_on(self.resolve_module(input, engine, on_progress))
243 }
244
245 #[deprecated(since = "0.601.0", note = "Use `resolve_module` instead")]
252 fn load_command_module(
253 &self,
254 cmd: &BinaryPackageCommand,
255 ) -> BoxFuture<'_, Result<Module, SpawnError>> {
256 self.resolve_module(ModuleInput::Command(Cow::Owned(cmd.clone())), None, None)
257 }
258
259 #[deprecated(since = "0.601.0", note = "Use `resolve_module_sync` instead")]
261 fn load_command_module_sync(&self, cmd: &BinaryPackageCommand) -> Result<Module, SpawnError> {
262 block_on(self.resolve_module(ModuleInput::Command(Cow::Borrowed(cmd)), None, None))
263 }
264
265 #[deprecated(since = "0.601.0", note = "Use `resolve_module` instead")]
269 fn load_module<'a>(&'a self, wasm: &'a [u8]) -> BoxFuture<'a, Result<Module, SpawnError>> {
270 self.resolve_module(ModuleInput::Bytes(Cow::Borrowed(wasm)), None, None)
271 }
272
273 #[deprecated(
275 since = "0.601.0",
276 note = "Use `load_command_module` or `load_hashed_module` instead - this method can have high overhead"
277 )]
278 fn load_module_sync(&self, wasm: &[u8]) -> Result<Module, SpawnError> {
279 block_on(self.resolve_module(ModuleInput::Bytes(Cow::Borrowed(wasm)), None, None))
280 }
281
282 fn load_hashed_module(
286 &self,
287 module: HashedModuleData,
288 engine: Option<&Engine>,
289 ) -> BoxFuture<'_, Result<Module, SpawnError>> {
290 self.resolve_module(ModuleInput::Hashed(Cow::Owned(module)), engine, None)
291 }
292
293 fn load_hashed_module_sync(
295 &self,
296 wasm: HashedModuleData,
297 engine: Option<&Engine>,
298 ) -> Result<Module, SpawnError> {
299 block_on(self.resolve_module(ModuleInput::Hashed(Cow::Owned(wasm)), engine, None))
300 }
301
302 fn on_taint(&self, _reason: TaintReason) {}
305
306 #[cfg(feature = "journal")]
309 fn read_only_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynReadableJournal>> + 'a> {
310 Box::new(std::iter::empty())
311 }
312
313 #[cfg(feature = "journal")]
315 fn writable_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynJournal>> + 'a> {
316 Box::new(std::iter::empty())
317 }
318
319 #[cfg(feature = "journal")]
322 fn active_journal(&self) -> Option<&'_ DynJournal> {
323 None
324 }
325}
326
327pub type DynRuntime = dyn Runtime + Send + Sync;
328
329#[tracing::instrument(level = "debug", skip_all)]
334pub async fn load_module(
335 engine: &Engine,
336 module_cache: &(dyn ModuleCache + Send + Sync),
337 input: ModuleInput<'_>,
338 on_progress: Option<ModuleLoadProgressReporter>,
339) -> Result<Module, crate::SpawnError> {
340 let wasm_hash = input.hash();
341
342 let result = if let Some(on_progress) = &on_progress {
343 module_cache
344 .load_with_progress(wasm_hash, engine, on_progress.clone())
345 .await
346 } else {
347 module_cache.load(wasm_hash, engine).await
348 };
349
350 match result {
351 Ok(module) => return Ok(module),
352 Err(CacheError::NotFound) => {}
353 Err(other) => {
354 tracing::warn!(
355 %wasm_hash,
356 error=&other as &dyn std::error::Error,
357 "Unable to load the cached module",
358 );
359 }
360 }
361
362 let res = if let Some(progress) = on_progress {
363 #[allow(unused_variables)]
364 let p = CompilationProgressCallback::new(move |p| {
365 progress.notify(ModuleLoadProgress::CompilingModule(p))
366 });
367 #[cfg(feature = "sys-default")]
368 {
369 use wasmer::sys::NativeEngineExt;
370 engine.new_module_with_progress(input.wasm(), p)
371 }
372 #[cfg(not(feature = "sys-default"))]
373 {
374 Module::new(&engine, input.wasm())
375 }
376 } else {
377 Module::new(&engine, input.wasm())
378 };
379
380 let module = res.map_err(|err| crate::SpawnError::CompileError {
381 module_hash: wasm_hash,
382 error: err,
383 })?;
384
385 if let Err(e) = module_cache.save(wasm_hash, engine, &module).await {
387 tracing::warn!(
388 %wasm_hash,
389 error=&e as &dyn std::error::Error,
390 "Unable to cache the compiled module",
391 );
392 }
393
394 Ok(module)
395}
396
397#[derive(Debug, Default)]
398pub struct DefaultTty {
399 state: Mutex<WasiTtyState>,
400}
401
402impl TtyBridge for DefaultTty {
403 fn reset(&self) {
404 let mut state = self.state.lock().unwrap();
405 state.echo = false;
406 state.line_buffered = false;
407 state.line_feeds = false
408 }
409
410 fn tty_get(&self) -> WasiTtyState {
411 let state = self.state.lock().unwrap();
412 state.clone()
413 }
414
415 fn tty_set(&self, tty_state: WasiTtyState) {
416 let mut state = self.state.lock().unwrap();
417 *state = tty_state;
418 }
419}
420
421#[derive(Debug, Clone)]
422pub struct PluggableRuntime {
423 pub rt: Arc<dyn VirtualTaskManager>,
424 pub networking: DynVirtualNetworking,
425 pub http_client: Option<DynHttpClient>,
426 pub package_loader: Arc<dyn PackageLoader + Send + Sync>,
427 pub source: Arc<dyn Source + Send + Sync>,
428 pub engine: Engine,
429 pub module_cache: Arc<dyn ModuleCache + Send + Sync>,
430 pub tty: Option<Arc<dyn TtyBridge + Send + Sync>>,
431 #[cfg(feature = "journal")]
432 pub read_only_journals: Vec<Arc<DynReadableJournal>>,
433 #[cfg(feature = "journal")]
434 pub writable_journals: Vec<Arc<DynJournal>>,
435}
436
437impl PluggableRuntime {
438 pub fn new(rt: Arc<dyn VirtualTaskManager>) -> Self {
439 cfg_if::cfg_if! {
441 if #[cfg(feature = "host-vnet")] {
442 let networking = Arc::new(virtual_net::host::LocalNetworking::default());
443 } else {
444 let networking = Arc::new(virtual_net::UnsupportedVirtualNetworking::default());
445 }
446 }
447 let http_client =
448 crate::http::default_http_client().map(|client| Arc::new(client) as DynHttpClient);
449
450 let loader = UnsupportedPackageLoader;
451
452 let mut source = MultiSource::default();
453 if let Some(client) = &http_client {
454 source.add_source(BackendSource::new(
455 BackendSource::WASMER_PROD_ENDPOINT.parse().unwrap(),
456 client.clone(),
457 ));
458 }
459
460 Self {
461 rt,
462 networking,
463 http_client,
464 engine: Default::default(),
465 tty: None,
466 source: Arc::new(source),
467 package_loader: Arc::new(loader),
468 module_cache: Arc::new(module_cache::in_memory()),
469 #[cfg(feature = "journal")]
470 read_only_journals: Vec::new(),
471 #[cfg(feature = "journal")]
472 writable_journals: Vec::new(),
473 }
474 }
475
476 pub fn set_networking_implementation<I>(&mut self, net: I) -> &mut Self
477 where
478 I: VirtualNetworking + Sync,
479 {
480 self.networking = Arc::new(net);
481 self
482 }
483
484 pub fn set_engine(&mut self, engine: Engine) -> &mut Self {
485 self.engine = engine;
486 self
487 }
488
489 pub fn set_tty(&mut self, tty: Arc<dyn TtyBridge + Send + Sync>) -> &mut Self {
490 self.tty = Some(tty);
491 self
492 }
493
494 pub fn set_module_cache(
495 &mut self,
496 module_cache: impl ModuleCache + Send + Sync + 'static,
497 ) -> &mut Self {
498 self.module_cache = Arc::new(module_cache);
499 self
500 }
501
502 pub fn set_source(&mut self, source: impl Source + Send + 'static) -> &mut Self {
503 self.source = Arc::new(source);
504 self
505 }
506
507 pub fn set_package_loader(
508 &mut self,
509 package_loader: impl PackageLoader + 'static,
510 ) -> &mut Self {
511 self.package_loader = Arc::new(package_loader);
512 self
513 }
514
515 pub fn set_http_client(
516 &mut self,
517 client: impl HttpClient + Send + Sync + 'static,
518 ) -> &mut Self {
519 self.http_client = Some(Arc::new(client));
520 self
521 }
522
523 #[cfg(feature = "journal")]
524 pub fn add_read_only_journal(&mut self, journal: Arc<DynReadableJournal>) -> &mut Self {
525 self.read_only_journals.push(journal);
526 self
527 }
528
529 #[cfg(feature = "journal")]
530 pub fn add_writable_journal(&mut self, journal: Arc<DynJournal>) -> &mut Self {
531 self.writable_journals.push(journal);
532 self
533 }
534}
535
536impl Runtime for PluggableRuntime {
537 fn networking(&self) -> &DynVirtualNetworking {
538 &self.networking
539 }
540
541 fn http_client(&self) -> Option<&DynHttpClient> {
542 self.http_client.as_ref()
543 }
544
545 fn package_loader(&self) -> Arc<dyn PackageLoader + Send + Sync> {
546 Arc::clone(&self.package_loader)
547 }
548
549 fn source(&self) -> Arc<dyn Source + Send + Sync> {
550 Arc::clone(&self.source)
551 }
552
553 fn engine(&self) -> Engine {
554 self.engine.clone()
555 }
556
557 fn new_store(&self) -> wasmer::Store {
558 wasmer::Store::new(self.engine.clone())
559 }
560
561 fn task_manager(&self) -> &Arc<dyn VirtualTaskManager> {
562 &self.rt
563 }
564
565 fn tty(&self) -> Option<&(dyn TtyBridge + Send + Sync)> {
566 self.tty.as_deref()
567 }
568
569 fn module_cache(&self) -> Arc<dyn ModuleCache + Send + Sync> {
570 self.module_cache.clone()
571 }
572
573 #[cfg(feature = "journal")]
574 fn read_only_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynReadableJournal>> + 'a> {
575 Box::new(self.read_only_journals.iter().cloned())
576 }
577
578 #[cfg(feature = "journal")]
579 fn writable_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynJournal>> + 'a> {
580 Box::new(self.writable_journals.iter().cloned())
581 }
582
583 #[cfg(feature = "journal")]
584 fn active_journal(&self) -> Option<&DynJournal> {
585 self.writable_journals.iter().last().map(|a| a.as_ref())
586 }
587}
588
589#[derive(Clone, Debug)]
592pub struct OverriddenRuntime {
593 inner: Arc<DynRuntime>,
594 task_manager: Option<Arc<dyn VirtualTaskManager>>,
595 networking: Option<DynVirtualNetworking>,
596 http_client: Option<DynHttpClient>,
597 package_loader: Option<Arc<dyn PackageLoader + Send + Sync>>,
598 source: Option<Arc<dyn Source + Send + Sync>>,
599 engine: Option<Engine>,
600 module_cache: Option<Arc<dyn ModuleCache + Send + Sync>>,
601 tty: Option<Arc<dyn TtyBridge + Send + Sync>>,
602 #[cfg(feature = "journal")]
603 pub read_only_journals: Option<Vec<Arc<DynReadableJournal>>>,
604 #[cfg(feature = "journal")]
605 pub writable_journals: Option<Vec<Arc<DynJournal>>>,
606}
607
608impl OverriddenRuntime {
609 pub fn new(inner: Arc<DynRuntime>) -> Self {
610 Self {
611 inner,
612 task_manager: None,
613 networking: None,
614 http_client: None,
615 package_loader: None,
616 source: None,
617 engine: None,
618 module_cache: None,
619 tty: None,
620 #[cfg(feature = "journal")]
621 read_only_journals: None,
622 #[cfg(feature = "journal")]
623 writable_journals: None,
624 }
625 }
626
627 pub fn with_task_manager(mut self, task_manager: Arc<dyn VirtualTaskManager>) -> Self {
628 self.task_manager.replace(task_manager);
629 self
630 }
631
632 pub fn with_networking(mut self, networking: DynVirtualNetworking) -> Self {
633 self.networking.replace(networking);
634 self
635 }
636
637 pub fn with_http_client(mut self, http_client: DynHttpClient) -> Self {
638 self.http_client.replace(http_client);
639 self
640 }
641
642 pub fn with_package_loader(
643 mut self,
644 package_loader: Arc<dyn PackageLoader + Send + Sync>,
645 ) -> Self {
646 self.package_loader.replace(package_loader);
647 self
648 }
649
650 pub fn with_source(mut self, source: Arc<dyn Source + Send + Sync>) -> Self {
651 self.source.replace(source);
652 self
653 }
654
655 pub fn with_engine(mut self, engine: Engine) -> Self {
656 self.engine.replace(engine);
657 self
658 }
659
660 pub fn with_module_cache(mut self, module_cache: Arc<dyn ModuleCache + Send + Sync>) -> Self {
661 self.module_cache.replace(module_cache);
662 self
663 }
664
665 pub fn with_tty(mut self, tty: Arc<dyn TtyBridge + Send + Sync>) -> Self {
666 self.tty.replace(tty);
667 self
668 }
669
670 #[cfg(feature = "journal")]
671 pub fn with_read_only_journals(mut self, journals: Vec<Arc<DynReadableJournal>>) -> Self {
672 self.read_only_journals.replace(journals);
673 self
674 }
675
676 #[cfg(feature = "journal")]
677 pub fn with_writable_journals(mut self, journals: Vec<Arc<DynJournal>>) -> Self {
678 self.writable_journals.replace(journals);
679 self
680 }
681}
682
683impl Runtime for OverriddenRuntime {
684 fn networking(&self) -> &DynVirtualNetworking {
685 if let Some(net) = self.networking.as_ref() {
686 net
687 } else {
688 self.inner.networking()
689 }
690 }
691
692 fn task_manager(&self) -> &Arc<dyn VirtualTaskManager> {
693 if let Some(rt) = self.task_manager.as_ref() {
694 rt
695 } else {
696 self.inner.task_manager()
697 }
698 }
699
700 fn source(&self) -> Arc<dyn Source + Send + Sync> {
701 if let Some(source) = self.source.clone() {
702 source
703 } else {
704 self.inner.source()
705 }
706 }
707
708 fn package_loader(&self) -> Arc<dyn PackageLoader + Send + Sync> {
709 if let Some(loader) = self.package_loader.clone() {
710 loader
711 } else {
712 self.inner.package_loader()
713 }
714 }
715
716 fn module_cache(&self) -> Arc<dyn ModuleCache + Send + Sync> {
717 if let Some(cache) = self.module_cache.clone() {
718 cache
719 } else {
720 self.inner.module_cache()
721 }
722 }
723
724 fn engine(&self) -> Engine {
725 if let Some(engine) = self.engine.clone() {
726 engine
727 } else {
728 self.inner.engine()
729 }
730 }
731
732 fn new_store(&self) -> wasmer::Store {
733 if let Some(engine) = self.engine.clone() {
734 wasmer::Store::new(engine)
735 } else {
736 self.inner.new_store()
737 }
738 }
739
740 fn http_client(&self) -> Option<&DynHttpClient> {
741 if let Some(client) = self.http_client.as_ref() {
742 Some(client)
743 } else {
744 self.inner.http_client()
745 }
746 }
747
748 fn tty(&self) -> Option<&(dyn TtyBridge + Send + Sync)> {
749 if let Some(tty) = self.tty.as_ref() {
750 Some(tty.deref())
751 } else {
752 self.inner.tty()
753 }
754 }
755
756 #[cfg(feature = "journal")]
757 fn read_only_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynReadableJournal>> + 'a> {
758 if let Some(journals) = self.read_only_journals.as_ref() {
759 Box::new(journals.iter().cloned())
760 } else {
761 self.inner.read_only_journals()
762 }
763 }
764
765 #[cfg(feature = "journal")]
766 fn writable_journals<'a>(&'a self) -> Box<dyn Iterator<Item = Arc<DynJournal>> + 'a> {
767 if let Some(journals) = self.writable_journals.as_ref() {
768 Box::new(journals.iter().cloned())
769 } else {
770 self.inner.writable_journals()
771 }
772 }
773
774 #[cfg(feature = "journal")]
775 fn active_journal(&self) -> Option<&'_ DynJournal> {
776 if let Some(journals) = self.writable_journals.as_ref() {
777 journals.iter().last().map(|a| a.as_ref())
778 } else {
779 self.inner.active_journal()
780 }
781 }
782}