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!("JIT support was requested but the 'jit' feature is disabled; running interpreter-only.");
94        }
95
96        let session = Self {
97            #[cfg(feature = "jit")]
98            jit_engine,
99            verbose,
100            stats: ExecutionStats::default(),
101            variables: HashMap::new(),
102            variable_array: Vec::new(),
103            variable_names: HashMap::new(),
104            workspace_values: HashMap::new(),
105            function_definitions: HashMap::new(),
106            source_pool: SourcePool::default(),
107            function_source_ids: HashMap::new(),
108            snapshot,
109            interrupt_flag: Arc::new(AtomicBool::new(false)),
110            is_executing: false,
111            async_input_handler: None,
112            callstack_limit: runmat_vm::DEFAULT_CALLSTACK_LIMIT,
113            error_namespace: runmat_vm::DEFAULT_ERROR_NAMESPACE.to_string(),
114            default_source_name: "<repl>".to_string(),
115            source_name_override: None,
116            telemetry_consent: true,
117            telemetry_client_id: None,
118            telemetry_platform: TelemetryPlatformInfo::default(),
119            telemetry_sink: None,
120            workspace_preview_tokens: HashMap::new(),
121            workspace_version: 0,
122            emit_fusion_plan: false,
123            compat_mode: CompatMode::Matlab,
124        };
125
126        runmat_vm::set_call_stack_limit(session.callstack_limit);
127
128        // Cache the shared plotting context (if a GPU provider is active) so the
129        // runtime can wire zero-copy render paths without instantiating another
130        // WebGPU device.
131        #[cfg(any(target_arch = "wasm32", not(target_arch = "wasm32")))]
132        {
133            if let Err(err) =
134                runmat_runtime::builtins::plotting::context::ensure_context_from_provider()
135            {
136                debug!("Plotting context unavailable during session init: {err}");
137            }
138        }
139
140        Ok(session)
141    }
142
143    pub(crate) fn current_source_name(&self) -> &str {
144        self.source_name_override
145            .as_deref()
146            .unwrap_or(&self.default_source_name)
147    }
148
149    #[cfg(target_arch = "wasm32")]
150    fn build_wasm_snapshot() -> Option<Arc<Snapshot>> {
151        use log::{info, warn};
152
153        info!("No snapshot provided; building stdlib snapshot inside wasm runtime");
154        let config = SnapshotConfig {
155            compression_enabled: false,
156            validation_enabled: false,
157            memory_mapping_enabled: false,
158            parallel_loading: false,
159            progress_reporting: false,
160            ..Default::default()
161        };
162
163        match SnapshotBuilder::new(config).build() {
164            Ok(snapshot) => {
165                info!("WASM snapshot build completed successfully");
166                Some(Arc::new(snapshot))
167            }
168            Err(err) => {
169                warn!("Failed to build stdlib snapshot in wasm runtime: {err}");
170                None
171            }
172        }
173    }
174
175    /// Load a snapshot from disk
176    #[cfg(not(target_arch = "wasm32"))]
177    fn load_snapshot(path: &Path) -> Result<Snapshot> {
178        let mut loader = SnapshotLoader::new(SnapshotConfig::default());
179        let (snapshot, _stats) = loader
180            .load(path)
181            .map_err(|e| anyhow::anyhow!("Failed to load snapshot: {}", e))?;
182        Ok(snapshot)
183    }
184
185    /// Load a snapshot from in-memory bytes
186    fn load_snapshot_from_bytes(bytes: &[u8]) -> Result<Snapshot> {
187        let mut loader = SnapshotLoader::new(SnapshotConfig::default());
188        let (snapshot, _stats) = loader
189            .load_from_bytes(bytes)
190            .map_err(|e| anyhow::anyhow!("Failed to load snapshot: {}", e))?;
191        Ok(snapshot)
192    }
193
194    /// Get snapshot information
195    pub fn snapshot_info(&self) -> Option<String> {
196        self.snapshot.as_ref().map(|snapshot| {
197            format!(
198                "Snapshot loaded: {} builtins, {} HIR functions, {} bytecode entries",
199                snapshot.builtins.functions.len(),
200                snapshot.hir_cache.functions.len(),
201                snapshot.bytecode_cache.stdlib_bytecode.len()
202            )
203        })
204    }
205
206    /// Check if a snapshot is loaded
207    pub fn has_snapshot(&self) -> bool {
208        self.snapshot.is_some()
209    }
210}