use typescript_types::{TsError, TsValue};
use crate::codegen::Instruction;
use super::{
exception_ops::ExceptionOperations,
function_ops::{ClassOperations, FunctionOperations},
memory::MemoryManager,
object_ops::{ArrayOperations, ObjectOperations},
perf::PerformanceMonitor,
};
pub struct ExecutionContext<'a> {
pub stack: &'a mut Vec<TsValue>,
pub memory: &'a mut MemoryManager,
pub perf_monitor: &'a mut PerformanceMonitor,
pub ip: usize,
}
pub struct InstructionExecutor;
impl InstructionExecutor {
pub fn push_constant(stack: &mut Vec<TsValue>, value: TsValue) {
stack.push(value);
}
pub fn load_variable(
name: &str,
stack: &mut Vec<TsValue>,
globals: &std::collections::HashMap<String, TsValue>,
call_stack: &[super::CallFrame],
) -> Result<(), TsError> {
if let Some(frame) = call_stack.last() {
if let Some(value) = frame.get_local(name) {
stack.push(value.clone());
return Ok(());
}
}
if let Some(value) = globals.get(name) {
stack.push(value.clone());
return Ok(());
}
Err(TsError::ReferenceError(format!("Variable '{}' is not defined", name)))
}
pub fn store_variable(
name: &str,
stack: &mut Vec<TsValue>,
globals: &mut std::collections::HashMap<String, TsValue>,
call_stack: &mut Vec<super::CallFrame>,
) -> Result<(), TsError> {
if let Some(value) = stack.pop() {
let value_clone = value.clone();
if let Some(frame) = call_stack.last_mut() {
if frame.get_local(name).is_some() {
frame.set_local(name, value);
stack.push(value_clone);
return Ok(());
}
}
globals.insert(name.to_string(), value);
stack.push(value_clone);
Ok(())
}
else {
Err(TsError::TypeError("Stack underflow".to_string()))
}
}
pub fn load_local(index: usize, stack: &mut Vec<TsValue>, call_stack: &[super::CallFrame]) -> Result<(), TsError> {
if let Some(frame) = call_stack.last() {
if let Some(value) = frame.get_local_by_index(index) {
stack.push(value.clone());
return Ok(());
}
}
Err(TsError::ReferenceError(format!("Local variable at index {} not found", index)))
}
pub fn store_local(index: usize, stack: &mut Vec<TsValue>, call_stack: &mut Vec<super::CallFrame>) -> Result<(), TsError> {
if let Some(value) = stack.pop() {
if let Some(frame) = call_stack.last_mut() {
frame.set_local_by_index(index, value);
return Ok(());
}
}
Err(TsError::TypeError("Failed to store local variable".to_string()))
}
pub fn jump_if_false(offset: i32, current_ip: usize, stack: &mut Vec<TsValue>) -> Result<usize, TsError> {
if let Some(value) = stack.pop() {
if !value.to_boolean() { Ok((current_ip as i32 + offset as i32) as usize) } else { Ok(current_ip + 1) }
}
else {
Err(TsError::TypeError("Stack underflow".to_string()))
}
}
pub fn jump(offset: i32, current_ip: usize) -> usize {
(current_ip as i32 + offset as i32) as usize
}
#[allow(clippy::too_many_arguments)]
pub fn execute_instruction(
instruction: &Instruction,
ctx: &mut ExecutionContext,
globals: &mut std::collections::HashMap<String, TsValue>,
functions: &mut std::collections::HashMap<String, super::Function>,
_modules: &mut std::collections::HashMap<String, super::ModuleInstance>,
call_stack: &mut Vec<super::CallFrame>,
exception_handlers: &mut Vec<super::ExceptionHandler>,
) -> Result<Option<TsValue>, TsError> {
match instruction {
Instruction::PushUndefined => {
ctx.stack.push(TsValue::Undefined);
}
Instruction::PushNull => {
ctx.stack.push(TsValue::Null);
}
Instruction::PushBoolean(b) => {
ctx.stack.push(TsValue::Boolean(*b));
}
Instruction::PushNumber(n) => {
ctx.stack.push(TsValue::Number(*n));
}
Instruction::PushString(s) => {
ctx.stack.push(TsValue::String(s.clone()));
}
Instruction::LoadVariable(name) => {
Self::load_variable(name, ctx.stack, globals, call_stack)?;
}
Instruction::StoreVariable(name) => {
Self::store_variable(name, ctx.stack, globals, call_stack)?;
}
Instruction::LoadLocal(index) => {
Self::load_local(*index, ctx.stack, call_stack)?;
}
Instruction::StoreLocal(index) => {
Self::store_local(*index, ctx.stack, call_stack)?;
}
Instruction::CreateObject => {
ObjectOperations::create_object(ctx.memory, ctx.stack);
}
Instruction::GetProperty => {
let property = ctx.stack.pop().ok_or_else(|| TsError::TypeError("Stack underflow".to_string()))?;
let object = ctx.stack.pop().ok_or_else(|| TsError::TypeError("Stack underflow".to_string()))?;
ObjectOperations::get_property(object, property, ctx.stack)?;
}
Instruction::SetProperty => {
let property = ctx.stack.pop().ok_or_else(|| TsError::TypeError("Stack underflow".to_string()))?;
let value = ctx.stack.pop().ok_or_else(|| TsError::TypeError("Stack underflow".to_string()))?;
let object = ctx.stack.pop().ok_or_else(|| TsError::TypeError("Stack underflow".to_string()))?;
ObjectOperations::set_property(object, property, value, ctx.stack)?;
}
Instruction::CreateArray => {
ArrayOperations::create_array(ctx.memory, ctx.stack);
}
Instruction::GetElement => {
let index = ctx.stack.pop().ok_or_else(|| TsError::TypeError("Stack underflow".to_string()))?;
let array = ctx.stack.pop().ok_or_else(|| TsError::TypeError("Stack underflow".to_string()))?;
ArrayOperations::get_element(array, index, ctx.stack)?;
}
Instruction::SetElement => {
let index = ctx.stack.pop().ok_or_else(|| TsError::TypeError("Stack underflow".to_string()))?;
let value = ctx.stack.pop().ok_or_else(|| TsError::TypeError("Stack underflow".to_string()))?;
let array = ctx.stack.pop().ok_or_else(|| TsError::TypeError("Stack underflow".to_string()))?;
ArrayOperations::set_element(array, index, value, ctx.memory, ctx.stack)?;
}
Instruction::CreateFunction(name, param_count) => {
FunctionOperations::create_function(name, *param_count, functions, ctx.stack)?;
}
Instruction::Call(arg_count) => {
FunctionOperations::call_function(*arg_count, ctx.stack, ctx.perf_monitor, call_stack.len())?;
}
Instruction::Return => {
let result = ctx.stack.pop().unwrap_or(TsValue::Undefined);
if let Some(frame) = call_stack.pop() {
ctx.ip = frame.return_ip;
}
return Ok(Some(result));
}
Instruction::CreateClass(name) => {
ClassOperations::create_class(name, ctx.stack)?;
}
Instruction::AddMethod(_name) => {
ClassOperations::add_method(ctx.stack)?;
}
Instruction::BinaryOp(_op) => {}
Instruction::UnaryOp(_op) => {}
Instruction::Jump(offset) => {
ctx.ip = Self::jump(*offset as i32, ctx.ip);
ctx.perf_monitor.record_instruction();
return Ok(None);
}
Instruction::JumpIfFalse(offset) => {
ctx.ip = Self::jump_if_false(*offset as i32, ctx.ip, ctx.stack)?;
ctx.perf_monitor.record_instruction();
return Ok(None);
}
Instruction::JumpLoop(_kind) => {}
Instruction::Throw => {
return ExceptionOperations::throw_exception(ctx.stack).map(|_| None);
}
Instruction::TryStart { handler_ip: _, finally_ip: _, exception_var: _ } => {}
Instruction::TryEnd => {
ExceptionOperations::try_end(exception_handlers)?;
}
Instruction::Pop => {
ctx.stack.pop();
}
Instruction::Dup => {
if let Some(value) = ctx.stack.last().cloned() {
ctx.stack.push(value);
}
}
Instruction::Swap => {
if ctx.stack.len() >= 2 {
let len = ctx.stack.len();
ctx.stack.swap(len - 1, len - 2);
}
}
Instruction::SetFunctionBody(_) => {
}
Instruction::SetClassBody(_) => {
}
Instruction::CreateTypeAlias(_) => {
}
Instruction::CreateInterface(_) => {
}
Instruction::ImportModule { .. } => {
}
Instruction::Export { .. } => {
}
}
ctx.perf_monitor.record_instruction();
Ok(None)
}
}