Skip to main content

runmat_core/session/
snapshot.rs

1use super::*;
2
3impl RunMatSession {
4    /// Create a new session
5    pub fn new() -> Result<Self> {
6        Self::with_options(true, false) // JIT enabled, verbose disabled
7    }
8
9    /// Create a new session with specific options
10    pub fn with_options(enable_jit: bool, verbose: bool) -> Result<Self> {
11        Self::from_snapshot(enable_jit, verbose, None)
12    }
13
14    /// Create a new session with snapshot loading
15    #[cfg(not(target_arch = "wasm32"))]
16    pub fn with_snapshot<P: AsRef<Path>>(
17        enable_jit: bool,
18        verbose: bool,
19        snapshot_path: Option<P>,
20    ) -> Result<Self> {
21        let snapshot = snapshot_path.and_then(|path| match Self::load_snapshot(path.as_ref()) {
22            Ok(snapshot) => {
23                info!(
24                    "Snapshot loaded successfully from {}",
25                    path.as_ref().display()
26                );
27                Some(Arc::new(snapshot))
28            }
29            Err(e) => {
30                warn!(
31                    "Failed to load snapshot from {}: {}, continuing without snapshot",
32                    path.as_ref().display(),
33                    e
34                );
35                None
36            }
37        });
38        Self::from_snapshot(enable_jit, verbose, snapshot)
39    }
40
41    /// Create a session using snapshot bytes (already fetched from disk or network)
42    pub fn with_snapshot_bytes(
43        enable_jit: bool,
44        verbose: bool,
45        snapshot_bytes: Option<&[u8]>,
46    ) -> Result<Self> {
47        let snapshot =
48            snapshot_bytes.and_then(|bytes| match Self::load_snapshot_from_bytes(bytes) {
49                Ok(snapshot) => {
50                    info!("Snapshot loaded successfully from in-memory bytes");
51                    Some(Arc::new(snapshot))
52                }
53                Err(e) => {
54                    warn!("Failed to load snapshot from bytes: {e}, continuing without snapshot");
55                    None
56                }
57            });
58        Self::from_snapshot(enable_jit, verbose, snapshot)
59    }
60
61    fn from_snapshot(
62        enable_jit: bool,
63        verbose: bool,
64        snapshot: Option<Arc<Snapshot>>,
65    ) -> Result<Self> {
66        #[cfg(target_arch = "wasm32")]
67        let snapshot = {
68            match snapshot {
69                some @ Some(_) => some,
70                None => Self::build_wasm_snapshot(),
71            }
72        };
73
74        #[cfg(feature = "jit")]
75        let jit_engine = if enable_jit {
76            match TurbineEngine::new() {
77                Ok(engine) => {
78                    info!("JIT compiler initialized successfully");
79                    Some(engine)
80                }
81                Err(e) => {
82                    warn!("JIT compiler initialization failed: {e}, falling back to interpreter");
83                    None
84                }
85            }
86        } else {
87            info!("JIT compiler disabled, using interpreter only");
88            None
89        };
90
91        #[cfg(not(feature = "jit"))]
92        if enable_jit {
93            info!(
94                "JIT support was requested but the 'jit' feature is disabled; running interpreter-only."
95            );
96        }
97
98        let session = Self {
99            #[cfg(feature = "jit")]
100            jit_engine,
101            verbose,
102            stats: ExecutionStats::default(),
103            variable_array: Vec::new(),
104            workspace_bindings: HashMap::new(),
105            workspace_values: HashMap::new(),
106            abi_workspace_handle: crate::abi::WorkspaceHandle(Uuid::new_v4()),
107            active_source_identity: None,
108            function_registry: runmat_vm::FunctionRegistry::default(),
109            next_semantic_function_id: 0,
110            source_pool: SourcePool::default(),
111            snapshot,
112            interrupt_flag: Arc::new(AtomicBool::new(false)),
113            is_executing: false,
114            async_input_handler: None,
115            callstack_limit: runmat_vm::DEFAULT_CALLSTACK_LIMIT,
116            error_namespace: runmat_vm::DEFAULT_ERROR_NAMESPACE.to_string(),
117            active_source_name: "<repl>".to_string(),
118            telemetry_consent: true,
119            telemetry_client_id: None,
120            telemetry_platform: TelemetryPlatformInfo::default(),
121            telemetry_sink: None,
122            workspace_preview_tokens: HashMap::new(),
123            workspace_version: 0,
124            emit_fusion_plan: false,
125            compat_mode: CompatMode::Matlab,
126            top_level_await_enabled: true,
127            dynamic_eval_enabled: true,
128            format_mode: runmat_builtins::FormatMode::default(),
129            pending_companion_source_discovery: None,
130        };
131
132        runmat_vm::set_call_stack_limit(session.callstack_limit);
133
134        // Cache the shared plotting context (if a GPU provider is active) so the
135        // runtime can wire zero-copy render paths without instantiating another
136        // WebGPU device.
137        #[cfg(any(target_arch = "wasm32", not(target_arch = "wasm32")))]
138        {
139            if let Err(err) =
140                runmat_runtime::builtins::plotting::context::ensure_context_from_provider()
141            {
142                debug!("Plotting context unavailable during session init: {err}");
143            }
144        }
145
146        Ok(session)
147    }
148
149    pub(crate) fn current_source_name(&self) -> &str {
150        &self.active_source_name
151    }
152
153    #[cfg(target_arch = "wasm32")]
154    fn build_wasm_snapshot() -> Option<Arc<Snapshot>> {
155        use log::{info, warn};
156
157        info!("No snapshot provided; building stdlib snapshot inside wasm runtime");
158        let config = SnapshotConfig {
159            compression_enabled: false,
160            validation_enabled: false,
161            memory_mapping_enabled: false,
162            parallel_loading: false,
163            progress_reporting: false,
164            ..Default::default()
165        };
166
167        match SnapshotBuilder::new(config).build() {
168            Ok(snapshot) => {
169                info!("WASM snapshot build completed successfully");
170                Some(Arc::new(snapshot))
171            }
172            Err(err) => {
173                warn!("Failed to build stdlib snapshot in wasm runtime: {err}");
174                None
175            }
176        }
177    }
178
179    /// Load a snapshot from disk
180    #[cfg(not(target_arch = "wasm32"))]
181    fn load_snapshot(path: &Path) -> Result<Snapshot> {
182        let mut loader = SnapshotLoader::new(SnapshotConfig::default());
183        let (snapshot, _stats) = loader
184            .load(path)
185            .map_err(|e| anyhow::anyhow!("Failed to load snapshot: {}", e))?;
186        Ok(snapshot)
187    }
188
189    /// Load a snapshot from in-memory bytes
190    fn load_snapshot_from_bytes(bytes: &[u8]) -> Result<Snapshot> {
191        let mut loader = SnapshotLoader::new(SnapshotConfig::default());
192        let (snapshot, _stats) = loader
193            .load_from_bytes(bytes)
194            .map_err(|e| anyhow::anyhow!("Failed to load snapshot: {}", e))?;
195        Ok(snapshot)
196    }
197
198    /// Get snapshot information
199    pub fn snapshot_info(&self) -> Option<String> {
200        self.snapshot.as_ref().map(|snapshot| {
201            format!(
202                "Snapshot loaded: {} builtins, {} HIR functions, {} bytecode entries",
203                snapshot.builtins.functions.len(),
204                snapshot.hir_cache.functions.len(),
205                snapshot.bytecode_cache.stdlib_bytecode.len()
206            )
207        })
208    }
209
210    /// Check if a snapshot is loaded
211    pub fn has_snapshot(&self) -> bool {
212        self.snapshot.is_some()
213    }
214}