1use std::collections::HashSet;
4
5use smallvec::SmallVec;
6
7use crate::interpreter::environment::Environment;
8use crate::interpreter::value::RValue;
9use crate::interpreter::{DiagnosticStyle, Interpreter};
10use crate::parser::ast::Expr;
11
12fn byte_offset_to_line(source: &str, offset: usize) -> usize {
14 source[..offset.min(source.len())]
15 .bytes()
16 .filter(|&b| b == b'\n')
17 .count()
18 + 1
19}
20
21#[derive(Debug, Clone, Default)]
25pub struct NativeBacktrace {
26 pub frames: Vec<usize>,
28}
29
30#[derive(Debug, Clone)]
32pub struct TraceEntry {
33 pub call: Option<Expr>,
35 pub native_backtrace: Option<NativeBacktrace>,
37 pub is_native_boundary: bool,
39 pub source_context: Option<(String, String)>,
41}
42
43pub(crate) fn retarget_call_expr(call_expr: Option<Expr>, target: &str) -> Option<Expr> {
44 match call_expr {
45 Some(Expr::Call { args, .. }) => Some(Expr::Call {
46 func: Box::new(Expr::Symbol(target.to_string())),
47 args,
48 span: None,
49 }),
50 _ => None,
51 }
52}
53
54#[derive(Debug, Clone)]
57pub(crate) struct S3DispatchContext {
58 pub generic: String,
59 pub classes: Vec<String>,
60 pub class_index: usize,
61 pub object: RValue,
62}
63
64#[derive(Debug, Clone)]
65pub(crate) struct CallFrame {
66 pub call: Option<Expr>,
67 pub function: RValue,
68 pub env: Environment,
69 pub formal_args: HashSet<String>,
70 pub supplied_args: HashSet<String>,
71 pub supplied_positional: SmallVec<[RValue; 4]>,
72 pub supplied_named: SmallVec<[(String, RValue); 2]>,
73 pub supplied_arg_count: usize,
74 pub is_native_boundary: bool,
77}
78
79#[derive(Clone, Copy)]
80pub struct BuiltinContext<'a> {
81 interpreter: &'a Interpreter,
82 env: &'a Environment,
83}
84
85impl<'a> BuiltinContext<'a> {
86 pub(crate) fn new(interpreter: &'a Interpreter, env: &'a Environment) -> Self {
87 Self { interpreter, env }
88 }
89
90 pub fn env(&self) -> &'a Environment {
91 self.env
92 }
93
94 pub fn interpreter(&self) -> &'a Interpreter {
95 self.interpreter
96 }
97
98 pub fn with_interpreter<F, R>(&self, f: F) -> R
99 where
100 F: FnOnce(&Interpreter) -> R,
101 {
102 f(self.interpreter)
103 }
104
105 pub fn write(&self, msg: &str) {
107 self.interpreter.write_stdout(msg);
108 }
109
110 pub fn write_err(&self, msg: &str) {
112 self.interpreter.write_stderr(msg);
113 }
114
115 pub fn write_err_colored(&self, msg: &str, style: DiagnosticStyle) {
121 self.interpreter.write_stderr_colored(msg, style);
122 }
123}
124
125impl Interpreter {
126 pub(crate) fn current_call_frame(&self) -> Option<CallFrame> {
127 self.call_stack.borrow().last().cloned()
128 }
129
130 pub(crate) fn call_frame(&self, which: usize) -> Option<CallFrame> {
131 self.call_stack
132 .borrow()
133 .get(which.saturating_sub(1))
134 .cloned()
135 }
136
137 pub(crate) fn call_frames(&self) -> Vec<CallFrame> {
138 self.call_stack.borrow().clone()
139 }
140
141 pub(crate) fn current_call_expr(&self) -> Option<Expr> {
142 self.current_call_frame().and_then(|frame| frame.call)
143 }
144
145 pub(crate) fn capture_traceback(&self) {
152 let current_gen = self.traceback_generation.get();
153 let captured_gen = self.traceback_captured_generation.get();
154 let mut tb = self.last_traceback.borrow_mut();
155
156 if current_gen == captured_gen && !tb.is_empty() {
158 return;
159 }
160 self.traceback_captured_generation.set(current_gen);
161 #[cfg(feature = "native")]
163 let pending_native = self.pending_native_backtrace.borrow_mut().take();
164
165 let source_ctx = self.source_stack.borrow().last().cloned();
167
168 let frames = self.call_stack.borrow();
169 let len = frames.len();
170 *tb = frames
171 .iter()
172 .enumerate()
173 .map(|(i, f)| {
174 let native_backtrace = {
175 #[cfg(feature = "native")]
176 {
177 if i == len - 1 {
179 pending_native.clone()
180 } else {
181 None
182 }
183 }
184 #[cfg(not(feature = "native"))]
185 {
186 let _ = i;
187 None
188 }
189 };
190 TraceEntry {
191 call: f.call.clone(),
192 native_backtrace,
193 is_native_boundary: f.is_native_boundary,
194 source_context: source_ctx.clone(),
195 }
196 })
197 .collect();
198 }
199
200 pub fn format_traceback(&self) -> Option<String> {
203 use crate::interpreter::value::deparse_expr;
204
205 let tb = self.last_traceback.borrow();
206 if tb.is_empty() {
207 return None;
208 }
209 let mut lines = Vec::with_capacity(tb.len());
210 for (i, entry) in tb.iter().enumerate().rev() {
211 if entry.is_native_boundary {
212 lines.push(" --- entered native code (Rf_eval callback) ---".to_string());
213 continue;
214 }
215 let call_str = match &entry.call {
216 Some(expr) => deparse_expr(expr),
217 None => "<anonymous>".to_string(),
218 };
219
220 let location = entry
222 .call
223 .as_ref()
224 .and_then(|expr| match expr {
225 Expr::Call {
226 span: Some(span), ..
227 } => Some(span),
228 _ => None,
229 })
230 .and_then(|span| {
231 let (filename, source_text) = entry.source_context.as_ref()?;
232 let line = byte_offset_to_line(source_text, span.start as usize);
233 Some(format!(" at {}:{}", filename, line))
234 })
235 .unwrap_or_default();
236
237 lines.push(format!("{}: {}{}", i + 1, call_str, location));
238
239 #[cfg(feature = "native")]
241 if let Some(ref bt) = entry.native_backtrace {
242 if !bt.frames.is_empty() {
243 let resolved = crate::interpreter::native::stacktrace::resolve_native_backtrace(
244 &bt.frames,
245 );
246 if !resolved.is_empty() {
247 lines.push(
248 crate::interpreter::native::stacktrace::format_native_frames(&resolved),
249 );
250 }
251 }
252 }
253 }
254 Some(lines.join("\n"))
255 }
256}