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