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(Rc::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(Rc::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<Rc<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            active_source_fullpath_name: None,
119            telemetry_consent: true,
120            telemetry_client_id: None,
121            telemetry_platform: TelemetryPlatformInfo::default(),
122            telemetry_sink: None,
123            workspace_preview_tokens: HashMap::new(),
124            workspace_version: 0,
125            emit_fusion_plan: false,
126            compat_mode: CompatMode::Matlab,
127            top_level_await_enabled: true,
128            dynamic_eval_enabled: true,
129            format_mode: runmat_builtins::FormatMode::default(),
130            pending_companion_source_discovery: None,
131        };
132
133        runmat_vm::set_call_stack_limit(session.callstack_limit);
134
135        // Cache the shared plotting context (if a GPU provider is active) so the
136        // runtime can wire zero-copy render paths without instantiating another
137        // WebGPU device.
138        #[cfg(any(target_arch = "wasm32", not(target_arch = "wasm32")))]
139        {
140            if let Err(err) =
141                runmat_runtime::builtins::plotting::context::ensure_context_from_provider()
142            {
143                debug!("Plotting context unavailable during session init: {err}");
144            }
145        }
146
147        Ok(session)
148    }
149
150    pub(crate) fn current_source_name(&self) -> &str {
151        &self.active_source_name
152    }
153
154    pub(crate) fn current_source_fullpath_name(&self) -> Option<&str> {
155        self.active_source_fullpath_name.as_deref()
156    }
157
158    #[cfg(target_arch = "wasm32")]
159    fn build_wasm_snapshot() -> Option<Rc<Snapshot>> {
160        use log::{info, warn};
161
162        info!("No snapshot provided; building stdlib snapshot inside wasm runtime");
163        let config = SnapshotConfig {
164            compression_enabled: false,
165            validation_enabled: false,
166            memory_mapping_enabled: false,
167            parallel_loading: false,
168            progress_reporting: false,
169            ..Default::default()
170        };
171
172        match SnapshotBuilder::new(config).build() {
173            Ok(snapshot) => {
174                info!("WASM snapshot build completed successfully");
175                Some(Rc::new(snapshot))
176            }
177            Err(err) => {
178                warn!("Failed to build stdlib snapshot in wasm runtime: {err}");
179                None
180            }
181        }
182    }
183
184    /// Load a snapshot from disk
185    #[cfg(not(target_arch = "wasm32"))]
186    fn load_snapshot(path: &Path) -> Result<Snapshot> {
187        let mut loader = SnapshotLoader::new(SnapshotConfig::default());
188        let (snapshot, _stats) = loader
189            .load(path)
190            .map_err(|e| anyhow::anyhow!("Failed to load snapshot: {}", e))?;
191        Ok(snapshot)
192    }
193
194    /// Load a snapshot from in-memory bytes
195    fn load_snapshot_from_bytes(bytes: &[u8]) -> Result<Snapshot> {
196        let mut loader = SnapshotLoader::new(SnapshotConfig::default());
197        let (snapshot, _stats) = loader
198            .load_from_bytes(bytes)
199            .map_err(|e| anyhow::anyhow!("Failed to load snapshot: {}", e))?;
200        Ok(snapshot)
201    }
202
203    /// Get snapshot information
204    pub fn snapshot_info(&self) -> Option<String> {
205        self.snapshot.as_ref().map(|snapshot| {
206            format!(
207                "Snapshot loaded: {} builtins, {} HIR functions, {} bytecode entries",
208                snapshot.builtins.functions.len(),
209                snapshot.hir_cache.functions.len(),
210                snapshot.bytecode_cache.stdlib_bytecode.len()
211            )
212        })
213    }
214
215    /// Check if a snapshot is loaded
216    pub fn has_snapshot(&self) -> bool {
217        self.snapshot.is_some()
218    }
219}