Skip to main content

runmat_core/session/
mod.rs

1use anyhow::Result;
2use runmat_builtins::{self, Value};
3use runmat_gc::{gc_configure, gc_stats, GcConfig};
4#[cfg(feature = "jit")]
5use tracing::warn;
6use tracing::{debug, info, info_span};
7
8use runmat_hir::{LoweringContext, LoweringResult, SourceId};
9use runmat_lexer::{tokenize_detailed, Token as LexToken};
10use runmat_parser::{parse_with_options, ParserOptions};
11use runmat_runtime::{build_runtime_error, gather_if_needed_async, RuntimeError};
12use runmat_runtime::{
13    runtime_export_workspace_state, runtime_import_workspace_state, WorkspaceReplayMode,
14};
15use runmat_time::Instant;
16#[cfg(feature = "jit")]
17use runmat_turbine::TurbineEngine;
18use std::collections::{HashMap, HashSet};
19use std::future::Future;
20use std::sync::{
21    atomic::{AtomicBool, Ordering},
22    Arc, Mutex,
23};
24use uuid::Uuid;
25
26use crate::execution::{
27    ExecutionStats, ExecutionStreamEntry, ExecutionStreamKind, InputRequest, InputRequestKind,
28    InputResponse, SharedAsyncInputHandler, StdinEvent, StdinEventKind,
29};
30use crate::fusion::{build_fusion_snapshot, FusionPlanSnapshot};
31use crate::profiling::{gather_profiling, reset_provider_telemetry};
32use crate::source_pool::{line_col_from_offset, SourcePool};
33use crate::telemetry::{TelemetryPlatformInfo, TelemetrySink};
34use crate::workspace::{
35    determine_display_label_from_context, execution_display_context, format_type_info,
36    gather_gpu_preview_values, gpu_dtype_label, gpu_size_bytes, last_emit_var_index,
37    last_store_var_index, slice_value_for_preview, workspace_entry, FinalStmtEmitDisposition,
38    MaterializedVariable, WorkspaceEntry, WorkspaceExportMode, WorkspaceMaterializeOptions,
39    WorkspaceMaterializeTarget, WorkspacePreview, WorkspaceResidency, WorkspaceSnapshot,
40    MATERIALIZE_DEFAULT_LIMIT,
41};
42use crate::{
43    approximate_size_bytes, matlab_class_name, numeric_dtype_label, preview_numeric_values,
44    value_shape, CompatMode, RunError,
45};
46
47mod compile;
48mod config;
49mod init;
50mod run;
51mod workspace;
52
53/// Host-agnostic RunMat execution session (parser + interpreter + optional JIT).
54pub struct RunMatSession {
55    /// JIT compiler engine (optional for fallback mode)
56    #[cfg(feature = "jit")]
57    jit_engine: Option<TurbineEngine>,
58    /// Verbose output for debugging
59    verbose: bool,
60    /// Execution statistics
61    stats: ExecutionStats,
62    /// Current variable array for bytecode execution
63    variable_array: Vec<Value>,
64    /// Current workspace bindings with stable ABI identity and current VM slot.
65    workspace_bindings: HashMap<String, SessionWorkspaceBinding>,
66    /// Persistent workspace values keyed by variable name
67    workspace_values: HashMap<String, Value>,
68    /// Stable ABI identity for this interactive workspace.
69    abi_workspace_handle: crate::abi::WorkspaceHandle,
70    /// Source identity for the active execution request (if source-scoped).
71    active_source_identity: Option<crate::abi::SourceIdentity>,
72    /// Semantic function registry persisted across interactive inputs.
73    function_registry: runmat_vm::FunctionRegistry,
74    next_semantic_function_id: usize,
75    /// Interned source pool for user-defined functions
76    source_pool: SourcePool,
77    /// Cooperative cancellation flag shared with the runtime.
78    interrupt_flag: Arc<AtomicBool>,
79    /// Tracks whether an execution is currently active.
80    is_executing: bool,
81    /// Optional async input handler (Phase 2). When set, stdin interactions are awaited
82    /// internally by `ExecuteFuture` rather than being surfaced as "pending requests".
83    async_input_handler: Option<SharedAsyncInputHandler>,
84    /// Maximum number of call stack frames to retain for diagnostics.
85    callstack_limit: usize,
86    /// Namespace prefix for runtime/semantic error identifiers.
87    error_namespace: String,
88    /// Active source name for diagnostics (set from execution requests).
89    active_source_name: String,
90    /// Resolved active source path for file-aware builtins and source discovery.
91    active_source_fullpath_name: Option<String>,
92    pub(crate) telemetry_consent: bool,
93    pub(crate) telemetry_client_id: Option<String>,
94    pub(crate) telemetry_platform: TelemetryPlatformInfo,
95    pub(crate) telemetry_sink: Option<Arc<dyn TelemetrySink>>,
96    workspace_preview_tokens: HashMap<Uuid, WorkspaceMaterializeTicket>,
97    workspace_version: u64,
98    emit_fusion_plan: bool,
99    compat_mode: CompatMode,
100    top_level_await_enabled: bool,
101    dynamic_eval_enabled: bool,
102    /// Persisted numeric display format for this session (survives across executions).
103    format_mode: runmat_builtins::FormatMode,
104    /// Persisted diary logging state for this session (survives across executions).
105    diary_state: runmat_runtime::console::DiaryStateSnapshot,
106    /// Preloaded companion statements discovered asynchronously by the request path.
107    pending_companion_source_discovery: Option<compile::CompanionSourceDiscovery>,
108}
109
110pub(crate) struct PreparedExecution {
111    ast: runmat_parser::Program,
112    lowering: LoweringResult,
113    analysis: runmat_mir::analysis::AnalysisStore,
114    pub(crate) bytecode: runmat_vm::Bytecode,
115    function_registry_after_success: runmat_vm::FunctionRegistry,
116    next_semantic_function_id_after_success: usize,
117}
118
119impl PreparedExecution {
120    #[cfg(test)]
121    pub(crate) fn lowering(&self) -> &LoweringResult {
122        &self.lowering
123    }
124
125    #[cfg(test)]
126    pub(crate) fn analysis(&self) -> &runmat_mir::analysis::AnalysisStore {
127        &self.analysis
128    }
129}
130
131#[derive(Debug, Clone)]
132pub(crate) struct SessionWorkspaceBinding {
133    pub(crate) key: crate::abi::WorkspaceBindingKey,
134    pub(crate) slot: usize,
135}
136
137#[derive(Debug, Clone)]
138struct WorkspaceMaterializeTicket {
139    name: String,
140}
141
142struct ActiveExecutionGuard {
143    flag: *mut bool,
144}
145
146impl ActiveExecutionGuard {
147    fn new(session: &mut RunMatSession) -> Result<Self> {
148        if session.is_executing {
149            Err(anyhow::anyhow!(
150                "RunMatSession is already executing another script"
151            ))
152        } else {
153            session.is_executing = true;
154            Ok(Self {
155                flag: &mut session.is_executing,
156            })
157        }
158    }
159}
160
161impl Drop for ActiveExecutionGuard {
162    fn drop(&mut self) {
163        unsafe {
164            if let Some(flag) = self.flag.as_mut() {
165                *flag = false;
166            }
167        }
168    }
169}
170
171struct SessionDiaryStateGuard {
172    session_state: *mut runmat_runtime::console::DiaryStateSnapshot,
173    previous_state: Option<runmat_runtime::console::DiaryStateSnapshot>,
174}
175
176impl SessionDiaryStateGuard {
177    fn new(session: &mut RunMatSession) -> Self {
178        let previous_state =
179            runmat_runtime::console::replace_diary_state(session.diary_state.clone());
180        Self {
181            session_state: &mut session.diary_state,
182            previous_state: Some(previous_state),
183        }
184    }
185}
186
187impl Drop for SessionDiaryStateGuard {
188    fn drop(&mut self) {
189        let current_state = runmat_runtime::console::diary_state_snapshot();
190        unsafe {
191            if let Some(session_state) = self.session_state.as_mut() {
192                *session_state = current_state;
193            }
194        }
195        if let Some(previous_state) = self.previous_state.take() {
196            runmat_runtime::console::replace_diary_state(previous_state);
197        }
198    }
199}
200
201impl Default for RunMatSession {
202    fn default() -> Self {
203        Self::new().expect("Failed to create default RunMat session")
204    }
205}