amberjs 1.16.1

JavaScript/TypeScript runtime built with Rust and V8
Documentation
// Call Stack Management
//
// This module provides functionality to capture and manage JavaScript
// call stacks, including stack frames, function information, and
/// variable scopes.
use rusty_v8 as v8;
use crate::debugger::{SourceLocation, DebugResult, v8_stubs::DebugExecutionState};
use std::collections::{HashMap, BTreeMap};
/// Stack frame information
#[derive(Debug, Clone)]
pub struct StackFrame {
    pub index: u32,
    pub function_name: String,
    pub script_id: String,
    pub script_name: String,
    pub line_number: u32,
    pub column_number: u32,
    pub is_eval: bool,
    pub is_constructor: bool,
    pub is_async: bool,
}
impl StackFrame {
    /// Get the source location
    pub fn get_location(&self) -> SourceLocation {
        SourceLocation {
            script_id: self.script_id.clone(),
            script_name: self.script_name.clone(),
            line_number: self.line_number,
            column_number: self.column_number,
        }
    }
    /// Check if this frame is at a specific location
    pub fn is_at_location(&self, script_id: &str, line_number: u32) -> bool {
        self.script_id == script_id && self.line_number == line_number
    }
}
/// Stack frame details (includes variable information)
#[derive(Debug, Clone)]
pub struct StackFrameInfo {
    pub frame: StackFrame,
    /// Function arguments
    pub arguments: Vec<FrameVariable>,
    /// Local variables
    pub locals: Vec<FrameVariable>,
    /// Closure variables (if applicable)
    pub closure: Option<Vec<FrameVariable>>,
}
/// Variable in a stack frame
#[derive(Debug, Clone)]
pub struct FrameVariable {
    pub name: String,
    pub value: String,
    pub type_name: String,
}
/// Call stack
#[derive(Debug, Clone)]
pub struct StackTrace {
    pub frames: Vec<StackFrame>,
    pub total_frames: usize,
    pub is_truncated: bool,
}
impl StackTrace {
    /// Create an empty stack trace
    pub fn new() -> Self {
        Self {
            frames: Vec::new(),
            total_frames: 0,
            is_truncated: false,
        }
    }
    /// Get the top frame (current execution point)
    pub fn current_frame(&self) -> Option<&StackFrame> {
        self.frames.first()
    }
    /// Get the bottom frame (script entry point)
    pub fn bottom_frame(&self) -> Option<&StackFrame> {
        self.frames.last()
    }
    /// Get frame at specific index
    pub fn get_frame(&self, index: usize) -> Option<&StackFrame> {
        self.frames.get(index)
    }
    /// Get number of frames
    pub fn frame_count(&self) -> usize {
        self.frames.len()
    }
    /// Check if stack is empty
    pub fn is_empty(&self) -> bool {
        self.frames.is_empty()
    }
    /// Find frames matching a script
    pub fn find_frames_in_script(&self, script_id: &str) -> Vec<&StackFrame> {
        self.frames
            .iter()
            .filter(|frame| frame.script_id == script_id)
            .collect()
    }
    /// Find frames at a specific location
    pub fn find_frames_at_location(
        &self,
        script_id: &str,
        line_number: u32,
    ) -> Vec<&StackFrame> {
        self.frames
            .iter()
            .filter(|frame| frame.is_at_location(script_id, line_number))
            .collect()
    }
    /// Get simplified stack trace (just function names and locations)
    pub fn simplified(&self) -> Vec<String> {
        self.frames
            .iter()
            .map(|frame| {
                format!(
                    "    at {} ({}:{}:{})",
                    frame.function_name,
                    frame.script_name,
                    frame.line_number,
                    frame.column_number
                )
            })
            .collect()
    }
    /// Convert to string representation
    pub fn to_string(&self) -> String {
        if self.frames.is_empty() {
            "Empty stack".to_string()
        } else {
            let mut result = String::new();
            result.push_str("Call Stack:\n");
            for (i, frame) in self.frames.iter().enumerate() {
                result.push_str(&format!(
                    "  {}: {} ({}:{}:{})\n",
                    i,
                    frame.function_name,
                    frame.script_name,
                    frame.line_number,
                    frame.column_number
                ));
            }
            if self.is_truncated {
                result.push_str(&format!(
                    "  ... and {} more frames\n",
                    self.total_frames - self.frames.len()));
            }
            result
        }
    }
}
impl Default for StackTrace {
    fn default() -> Self {
        Self::new()
    }
}
/// Stack frame builder for constructing frames from V8 data
pub struct StackFrameBuilder {
    frame_index: u32,
}
impl StackFrameBuilder {
    /// Create a new builder
    pub fn new() -> Self {
        Self { frame_index: 0 }
    }
    /// Build a stack frame from V8 execution state
    pub fn build_from_v8(
        &mut self,
        exec_state: &v8::Global<DebugExecutionState>,
    ) -> DebugResult<StackFrame> {
        // This would integrate with V8's DebugExecutionState API
        // For now, return a placeholder implementation
        let frame: _ = StackFrame {
            index: self.frame_index,
            function_name: "Unknown".to_string(),
            script_id: "Unknown".to_string(),
            script_name: "Unknown".to_string(),
            line_number: 0,
            column_number: 0,
            is_eval: false,
            is_constructor: false,
            is_async: false,
        };
        self.frame_index += 1;
        DebugResult::ok(frame)
    }
    /// Build stack trace from V8 execution state
    pub fn build_stack_trace_from_v8(
        &mut self,
        exec_state: &v8::Global<DebugExecutionState>,
        max_frames: usize,
    ) -> DebugResult<StackTrace> {
        let mut frames = Vec::new();
        let mut is_truncated = false;
        // Get total frame count from V8
        // Note: V8 DebugExecutionState API has changed in rusty_v8 0.22
        // This is a placeholder implementation
        // TODO: Implement proper frame counting with V8
        let total_frames: _ = 0;
        // Build frames up to max_frames
        for i in 0..std::cmp::min(max_frames, total_frames) {
            // This would use V8's DebugExecutionState API
            // For now, use placeholder
            let frame: _ = StackFrame {
                index: i as u32,
                function_name: format!("frame_{}", i),
                script_id: "script_0".to_string(),
                script_name: "unknown.js".to_string(),
                line_number: 1,
                column_number: 1,
                is_eval: false,
                is_constructor: false,
                is_async: false,
            };
            frames.push(frame);
        }
        if total_frames > max_frames {
            is_truncated = true;
        }
        DebugResult::ok(StackTrace {
            frames,
            total_frames,
            is_truncated,
        })
    }
    /// Reset builder
    pub fn reset(&mut self) {
        self.frame_index = 0;
    }
}
impl Default for StackFrameBuilder {
    fn default() -> Self {
        Self::new()
    }
}
/// Stack traversal utilities
pub struct StackTraverser {
    current_index: usize,
}
impl StackTraverser {
    /// Create a new traverser
    pub fn new() -> Self {
        Self { current_index: 0 }
    }
    /// Move to next frame
    pub fn next_frame<'a>(&mut self, stack: &'a StackTrace) -> Option<&'a StackFrame> {
        if self.current_index < stack.frames.len() {
            let frame: _ = &stack.frames[self.current_index];
            self.current_index += 1;
            Some(frame)
        } else {
            None
        }
    }
    /// Move to previous frame
    pub fn previous_frame<'a>(&mut self, stack: &'a StackTrace) -> Option<&'a StackFrame> {
        if self.current_index > 0 {
            self.current_index -= 1;
            Some(&stack.frames[self.current_index])
        } else {
            None
        }
    }
    /// Get current frame
    pub fn current_frame<'a>(&self, stack: &'a StackTrace) -> Option<&'a StackFrame> {
        if self.current_index < stack.frames.len() {
            Some(&stack.frames[self.current_index])
        } else {
            None
        }
    }
    /// Reset to beginning
    pub fn reset(&mut self) {
        self.current_index = 0;
    }
    /// Jump to specific frame
    pub fn jump_to_frame<'a>(&mut self, stack: &'a StackTrace, index: usize) -> Option<&'a StackFrame> {
        if index < stack.frames.len() {
            self.current_index = index;
            Some(&stack.frames[index])
        } else {
            None
        }
    }
}
impl Default for StackTraverser {
    fn default() -> Self {
        Self::new()
    }
}