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