use crate::engine::facade::{StdValFactory, ValFactory};
use crate::engine::types::{PhpType, PhpValue, Val};
pub(crate) const TEMP_VAR_TYPE: PhpType = PhpType::Undef;
pub fn temp_var_ref(index: u32) -> Val {
Val::new(PhpValue::Long(index as i64), TEMP_VAR_TYPE)
}
pub fn var_ref(name: &str) -> Val {
let clean = if name.starts_with('$') {
&name[1..]
} else {
name
};
Val::new(
PhpValue::String(Box::new(crate::engine::string::string_init(clean, false))),
TEMP_VAR_TYPE,
)
}
pub(crate) fn is_temp_ref(z: &Val) -> bool {
z.get_type() == TEMP_VAR_TYPE && matches!(z.value, PhpValue::Long(_))
}
pub(crate) fn is_var_ref(z: &Val) -> bool {
z.get_type() == TEMP_VAR_TYPE && matches!(z.value, PhpValue::String(_))
}
pub(crate) fn resolve_operand(operand: &Val, execute_data: &ExecuteData) -> Val {
if is_temp_ref(operand) {
if let PhpValue::Long(idx) = &operand.value {
return execute_data.get_temp(*idx as usize);
}
}
if is_var_ref(operand) {
if let PhpValue::String(name) = &operand.value {
let n = name.as_str();
let clean = if n.starts_with('$') { &n[1..] } else { n };
return execute_data.get_var(clean);
}
}
if operand.get_type() == PhpType::ConstantAst {
if let PhpValue::String(name) = &operand.value {
let name_str = name.as_str();
if let Some(v) = execute_data.constants.get(name_str) {
return clone_val(v);
}
return Val::new(
PhpValue::String(Box::new(crate::engine::string::string_init(name_str, false))),
PhpType::String,
);
}
}
clone_val(operand)
}
pub(crate) fn result_slot(op: &super::opcodes::Op) -> Option<usize> {
if is_temp_ref(&op.result) {
if let PhpValue::Long(idx) = &op.result.value {
return Some(*idx as usize);
}
}
None
}
pub(crate) fn clone_val(source: &Val) -> Val {
StdValFactory::clone_val(source)
}
pub enum ExecResult {
Continue,
Jump(u32),
Return(Val),
}
#[derive(Debug)]
pub struct ExecuteData {
pub op_array: Option<super::opcodes::OpArray>,
pub current_op: usize,
pub symbol_table: Option<crate::engine::types::PhpArray>,
pub function_table: Option<std::sync::Arc<dyn std::any::Any + Send + Sync>>,
pub temp_vars: Vec<Val>,
pub call_args: Vec<Val>,
pub included_files: std::collections::HashSet<String>,
pub class_table: std::collections::HashMap<String, crate::engine::types::ClassEntry>,
pub constants: std::collections::HashMap<String, Val>,
pub current_script_dir: Option<String>,
pub exit_requested: Option<i64>,
pub error_handler: Option<String>,
pub exception_handler: Option<String>,
pub shutdown_functions: Vec<String>,
}
impl ExecuteData {
pub fn new() -> Self {
Self {
op_array: None,
current_op: 0,
symbol_table: Some(crate::engine::types::PhpArray::new()),
function_table: None,
temp_vars: Vec::new(),
call_args: Vec::new(),
included_files: std::collections::HashSet::new(),
class_table: std::collections::HashMap::new(),
constants: std::collections::HashMap::new(),
current_script_dir: None,
exit_requested: None,
error_handler: None,
exception_handler: None,
shutdown_functions: Vec::new(),
}
}
pub fn ensure_temp_slots(&mut self, n: usize) {
if self.temp_vars.len() < n {
self.temp_vars
.resize_with(n, || Val::new(PhpValue::Long(0), PhpType::Null));
}
}
pub fn get_temp(&self, index: usize) -> Val {
self.temp_vars
.get(index)
.map(|z| clone_val(z))
.unwrap_or_else(|| Val::new(PhpValue::Long(0), PhpType::Null))
}
pub fn set_temp(&mut self, index: usize, val: Val) {
if index >= self.temp_vars.len() {
self.ensure_temp_slots(index + 1);
}
self.temp_vars[index] = val;
}
pub fn get_var(&self, name: &str) -> Val {
if let Some(ref st) = self.symbol_table {
let key = crate::engine::string::string_init(name, false);
if let Some(val) = crate::engine::hash::hash_find(st, &key) {
return clone_val(val);
}
}
Val::new(PhpValue::Long(0), PhpType::Null)
}
pub fn set_var(&mut self, name: &str, val: Val) {
if let Some(ref mut st) = self.symbol_table {
let key = crate::engine::string::string_init(name, false);
let key_box = Box::new(key);
let _ = crate::engine::hash::hash_add_or_update(st, Some(&*key_box), 0, val, 0);
}
}
pub fn remove_var(&mut self, name: &str) {
if let Some(ref mut st) = self.symbol_table {
let key = crate::engine::string::string_init(name, false);
let _ = crate::engine::hash::hash_del(st, &key);
}
}
}