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