1use anyhow::Context as _;
2use spin_common::{ui::quoted_path, url::parse_file_url};
3use spin_compose::ComponentSourceLoaderFs;
4use spin_core::{async_trait, wasmtime, Component};
5use spin_factors::{AppComponent, RuntimeFactors};
6
7#[derive(Default)]
8pub struct ComponentLoader {
9 _private: (),
10 #[cfg(feature = "unsafe-aot-compilation")]
11 aot_compilation_enabled: bool,
12}
13
14impl ComponentLoader {
15 pub fn new() -> Self {
17 Self::default()
18 }
19
20 #[cfg(feature = "unsafe-aot-compilation")]
45 pub unsafe fn enable_loading_aot_compiled_components(&mut self) {
46 self.aot_compilation_enabled = true;
47 }
48
49 #[cfg(feature = "unsafe-aot-compilation")]
50 fn load_precompiled_component(
51 &self,
52 engine: &wasmtime::Engine,
53 path: &std::path::Path,
54 ) -> anyhow::Result<Component> {
55 assert!(self.aot_compilation_enabled);
56 match engine.detect_precompiled_file(path)? {
57 Some(wasmtime::Precompiled::Component) => unsafe {
58 Component::deserialize_file(engine, path)
59 },
60 Some(wasmtime::Precompiled::Module) => {
61 anyhow::bail!("expected AOT compiled component but found module");
62 }
63 None => {
64 anyhow::bail!("expected AOT compiled component but found other data");
65 }
66 }
67 }
68}
69
70#[async_trait]
71impl<T: RuntimeFactors, U> spin_factors_executor::ComponentLoader<T, U> for ComponentLoader {
72 async fn load_component(
73 &self,
74 engine: &wasmtime::Engine,
75 component: &AppComponent,
76 ) -> anyhow::Result<Component> {
77 let source = component
78 .source()
79 .content
80 .source
81 .as_ref()
82 .context("LockedComponentSource missing source field")?;
83 let path = parse_file_url(source)?;
84
85 #[cfg(feature = "unsafe-aot-compilation")]
86 if self.aot_compilation_enabled {
87 return self
88 .load_precompiled_component(engine, &path)
89 .with_context(|| format!("error deserializing component from {path:?}"));
90 }
91
92 let composed = spin_compose::compose(&ComponentSourceLoaderFs, component.locked)
93 .await
94 .with_context(|| {
95 format!(
96 "failed to resolve dependencies for component {:?}",
97 component.locked.id
98 )
99 })?;
100
101 spin_core::Component::new(engine, composed)
102 .with_context(|| format!("failed to compile component from {}", quoted_path(&path)))
103 }
104}