use crate::runtime::Value;
use smallvec::{smallvec, SmallVec};
use std::collections::HashMap;
#[derive(Clone, Debug, PartialEq)]
pub enum CacheState {
Uninitialized,
Monomorphic,
Polymorphic,
Megamorphic,
}
#[derive(Clone, Debug)]
pub struct CacheEntry {
pub type_id: std::any::TypeId,
pub field_name: String,
pub cached_result: Value,
pub hit_count: u32,
}
#[derive(Clone, Debug)]
pub struct InlineCache {
state: CacheState,
entries: SmallVec<[CacheEntry; 2]>,
total_hits: u32,
total_misses: u32,
}
impl InlineCache {
pub fn new() -> Self {
Self {
state: CacheState::Uninitialized,
entries: smallvec![],
total_hits: 0,
total_misses: 0,
}
}
pub fn state(&self) -> &CacheState {
&self.state
}
pub fn entry_count(&self) -> usize {
self.entries.len()
}
pub fn lookup(&mut self, obj: &Value, field_name: &str) -> Option<Value> {
let type_id = obj.type_id();
for entry in &mut self.entries {
if entry.type_id == type_id && entry.field_name == field_name {
entry.hit_count += 1;
self.total_hits += 1;
return Some(entry.cached_result.clone());
}
}
self.total_misses += 1;
None
}
pub fn insert(&mut self, obj: &Value, field_name: String, result: Value) {
let type_id = obj.type_id();
let entry = CacheEntry {
type_id,
field_name,
cached_result: result,
hit_count: 1,
};
match self.entries.len() {
0 => {
self.state = CacheState::Monomorphic;
self.entries.push(entry);
}
1..=3 => {
self.state = CacheState::Polymorphic;
self.entries.push(entry);
}
_ => {
self.state = CacheState::Megamorphic;
if let Some(min_idx) = self
.entries
.iter()
.enumerate()
.min_by_key(|(_, e)| e.hit_count)
.map(|(i, _)| i)
{
self.entries[min_idx] = entry;
}
}
}
}
pub fn hit_rate(&self) -> f64 {
let total = self.total_hits + self.total_misses;
if total == 0 {
0.0
} else {
f64::from(self.total_hits) / f64::from(total)
}
}
pub fn total_hits(&self) -> u32 {
self.total_hits
}
pub fn total_misses(&self) -> u32 {
self.total_misses
}
}
impl Default for InlineCache {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct OperationFeedback {
pub left_types: SmallVec<[std::any::TypeId; 4]>,
pub right_types: SmallVec<[std::any::TypeId; 4]>,
pub result_types: SmallVec<[std::any::TypeId; 4]>,
pub type_counts: HashMap<(std::any::TypeId, std::any::TypeId), u32>,
pub total_count: u32,
}
impl OperationFeedback {
pub fn new() -> Self {
Self {
left_types: smallvec![],
right_types: smallvec![],
result_types: smallvec![],
type_counts: HashMap::new(),
total_count: 0,
}
}
pub fn is_monomorphic(&self) -> bool {
self.left_types.len() == 1 && self.right_types.len() == 1
}
}
impl Default for OperationFeedback {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct VariableTypeFeedback {
pub assigned_types: SmallVec<[std::any::TypeId; 4]>,
pub transitions: HashMap<std::any::TypeId, HashMap<std::any::TypeId, u32>>,
pub dominant_type: Option<std::any::TypeId>,
pub stability_score: f64,
}
impl VariableTypeFeedback {
pub fn new() -> Self {
Self {
assigned_types: smallvec![],
transitions: HashMap::new(),
dominant_type: None,
stability_score: 1.0,
}
}
pub fn is_stable(&self) -> bool {
self.stability_score > 0.8
}
}
impl Default for VariableTypeFeedback {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct CallSiteFeedback {
pub arg_type_patterns: SmallVec<[Vec<std::any::TypeId>; 4]>,
pub return_types: SmallVec<[std::any::TypeId; 4]>,
pub call_count: u32,
pub called_functions: HashMap<String, u32>,
}
impl CallSiteFeedback {
pub fn new() -> Self {
Self {
arg_type_patterns: smallvec![],
return_types: smallvec![],
call_count: 0,
called_functions: HashMap::new(),
}
}
pub fn is_monomorphic(&self) -> bool {
self.arg_type_patterns.len() == 1 && self.return_types.len() == 1
}
}
impl Default for CallSiteFeedback {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct TypeFeedback {
operation_sites: HashMap<usize, OperationFeedback>,
variable_types: HashMap<String, VariableTypeFeedback>,
call_sites: HashMap<usize, CallSiteFeedback>,
total_samples: u64,
}
impl TypeFeedback {
pub fn new() -> Self {
Self {
operation_sites: HashMap::new(),
variable_types: HashMap::new(),
call_sites: HashMap::new(),
total_samples: 0,
}
}
pub fn record_binary_op(
&mut self,
site_id: usize,
left: &Value,
right: &Value,
result: &Value,
) {
let left_type = left.type_id();
let right_type = right.type_id();
let result_type = result.type_id();
let feedback = self.operation_sites.entry(site_id).or_default();
if !feedback.left_types.contains(&left_type) {
feedback.left_types.push(left_type);
}
if !feedback.right_types.contains(&right_type) {
feedback.right_types.push(right_type);
}
if !feedback.result_types.contains(&result_type) {
feedback.result_types.push(result_type);
}
let type_pair = (left_type, right_type);
*feedback.type_counts.entry(type_pair).or_insert(0) += 1;
feedback.total_count += 1;
self.total_samples += 1;
}
pub fn record_variable_assignment(&mut self, var_name: &str, new_type: std::any::TypeId) {
let feedback = self.variable_types.entry(var_name.to_string()).or_default();
if let Some(prev_type) = feedback.dominant_type {
if prev_type != new_type {
feedback
.transitions
.entry(prev_type)
.or_default()
.entry(new_type)
.and_modify(|count| *count += 1)
.or_insert(1);
}
}
if !feedback.assigned_types.contains(&new_type) {
feedback.assigned_types.push(new_type);
}
feedback.dominant_type = Some(new_type);
feedback.stability_score = if feedback.assigned_types.len() == 1 {
1.0 } else {
1.0 / f64::from(u32::try_from(feedback.assigned_types.len()).unwrap_or(u32::MAX))
};
}
pub fn record_function_call(
&mut self,
site_id: usize,
func_name: &str,
args: &[Value],
result: &Value,
) {
let arg_types: Vec<std::any::TypeId> = args.iter().map(Value::type_id).collect();
let return_type = result.type_id();
let feedback = self.call_sites.entry(site_id).or_default();
if !feedback
.arg_type_patterns
.iter()
.any(|pattern| pattern == &arg_types)
{
feedback.arg_type_patterns.push(arg_types);
}
if !feedback.return_types.contains(&return_type) {
feedback.return_types.push(return_type);
}
*feedback
.called_functions
.entry(func_name.to_string())
.or_insert(0) += 1;
feedback.call_count += 1;
}
pub fn get_specialization_candidates(&self) -> Vec<SpecializationCandidate> {
let mut candidates = Vec::new();
for (&site_id, feedback) in &self.operation_sites {
if feedback.is_monomorphic() && feedback.total_count > 10 {
candidates.push(SpecializationCandidate {
kind: SpecializationKind::BinaryOperation {
site_id,
left_type: feedback.left_types[0],
right_type: feedback.right_types[0],
},
confidence: 1.0,
benefit_score: f64::from(feedback.total_count),
});
}
}
for (var_name, feedback) in &self.variable_types {
if feedback.is_stable() {
if let Some(dominant_type) = feedback.dominant_type {
candidates.push(SpecializationCandidate {
kind: SpecializationKind::Variable {
name: var_name.clone(),
specialized_type: dominant_type,
},
confidence: feedback.stability_score,
benefit_score: feedback.stability_score * 100.0,
});
}
}
}
for (&site_id, feedback) in &self.call_sites {
if feedback.is_monomorphic() && feedback.call_count > 5 {
candidates.push(SpecializationCandidate {
kind: SpecializationKind::FunctionCall {
site_id,
arg_types: feedback.arg_type_patterns[0].clone(),
return_type: feedback.return_types[0],
},
confidence: 1.0,
benefit_score: f64::from(feedback.call_count * 10),
});
}
}
candidates.sort_by(|a, b| {
b.benefit_score
.partial_cmp(&a.benefit_score)
.unwrap_or(std::cmp::Ordering::Equal)
});
candidates
}
pub fn get_statistics(&self) -> TypeFeedbackStats {
let monomorphic_sites = self
.operation_sites
.values()
.filter(|f| f.is_monomorphic())
.count();
let stable_variables = self
.variable_types
.values()
.filter(|f| f.is_stable())
.count();
let monomorphic_calls = self
.call_sites
.values()
.filter(|f| f.is_monomorphic())
.count();
TypeFeedbackStats {
total_operation_sites: self.operation_sites.len(),
monomorphic_operation_sites: monomorphic_sites,
total_variables: self.variable_types.len(),
stable_variables,
total_call_sites: self.call_sites.len(),
monomorphic_call_sites: monomorphic_calls,
total_samples: self.total_samples,
}
}
pub fn total_samples(&self) -> u64 {
self.total_samples
}
}
impl Default for TypeFeedback {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct SpecializationCandidate {
pub kind: SpecializationKind,
pub confidence: f64,
pub benefit_score: f64,
}
#[derive(Clone, Debug)]
pub enum SpecializationKind {
BinaryOperation {
site_id: usize,
left_type: std::any::TypeId,
right_type: std::any::TypeId,
},
Variable {
name: String,
specialized_type: std::any::TypeId,
},
FunctionCall {
site_id: usize,
arg_types: Vec<std::any::TypeId>,
return_type: std::any::TypeId,
},
}
#[derive(Clone, Debug)]
pub struct TypeFeedbackStats {
pub total_operation_sites: usize,
pub monomorphic_operation_sites: usize,
pub total_variables: usize,
pub stable_variables: usize,
pub total_call_sites: usize,
pub monomorphic_call_sites: usize,
pub total_samples: u64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_inline_cache_new() {
let cache = InlineCache::new();
assert_eq!(*cache.state(), CacheState::Uninitialized);
assert_eq!(cache.entry_count(), 0);
assert_eq!(cache.total_hits(), 0);
assert_eq!(cache.total_misses(), 0);
}
#[test]
fn test_inline_cache_default() {
let cache = InlineCache::default();
assert_eq!(*cache.state(), CacheState::Uninitialized);
}
#[test]
fn test_inline_cache_lookup_miss() {
let mut cache = InlineCache::new();
let value = Value::Integer(42);
assert!(cache.lookup(&value, "field").is_none());
assert_eq!(cache.total_misses(), 1);
}
#[test]
fn test_inline_cache_insert_monomorphic() {
let mut cache = InlineCache::new();
let obj = Value::Integer(1);
cache.insert(&obj, "x".to_string(), Value::Integer(10));
assert_eq!(*cache.state(), CacheState::Monomorphic);
assert_eq!(cache.entry_count(), 1);
}
#[test]
fn test_inline_cache_lookup_hit() {
let mut cache = InlineCache::new();
let obj = Value::Integer(1);
cache.insert(&obj, "x".to_string(), Value::Integer(10));
let result = cache.lookup(&obj, "x");
assert!(result.is_some());
assert_eq!(result.unwrap(), Value::Integer(10));
assert_eq!(cache.total_hits(), 1);
}
#[test]
fn test_inline_cache_polymorphic() {
let mut cache = InlineCache::new();
cache.insert(&Value::Integer(1), "a".to_string(), Value::Integer(1));
cache.insert(&Value::Float(1.0), "b".to_string(), Value::Float(2.0));
assert_eq!(*cache.state(), CacheState::Polymorphic);
assert_eq!(cache.entry_count(), 2);
}
#[test]
fn test_inline_cache_megamorphic() {
let mut cache = InlineCache::new();
for i in 0..5 {
cache.insert(
&Value::Integer(i),
format!("field{i}"),
Value::Integer(i * 10),
);
}
assert_eq!(*cache.state(), CacheState::Megamorphic);
}
#[test]
fn test_inline_cache_hit_rate_zero() {
let cache = InlineCache::new();
assert_eq!(cache.hit_rate(), 0.0);
}
#[test]
fn test_inline_cache_hit_rate() {
let mut cache = InlineCache::new();
let obj = Value::Integer(1);
cache.insert(&obj, "x".to_string(), Value::Integer(10));
cache.lookup(&obj, "x"); cache.lookup(&obj, "y");
assert!(cache.hit_rate() > 0.0 && cache.hit_rate() < 1.0);
}
#[test]
fn test_cache_state_equality() {
assert_eq!(CacheState::Uninitialized, CacheState::Uninitialized);
assert_eq!(CacheState::Monomorphic, CacheState::Monomorphic);
assert_ne!(CacheState::Monomorphic, CacheState::Polymorphic);
}
#[test]
fn test_operation_feedback_new() {
let feedback = OperationFeedback::new();
assert!(feedback.left_types.is_empty());
assert!(feedback.right_types.is_empty());
assert_eq!(feedback.total_count, 0);
}
#[test]
fn test_operation_feedback_default() {
let feedback = OperationFeedback::default();
assert!(feedback.left_types.is_empty());
}
#[test]
fn test_operation_feedback_is_monomorphic_empty() {
let feedback = OperationFeedback::new();
assert!(!feedback.is_monomorphic());
}
#[test]
fn test_variable_type_feedback_new() {
let feedback = VariableTypeFeedback::new();
assert!(feedback.assigned_types.is_empty());
assert!(feedback.dominant_type.is_none());
assert_eq!(feedback.stability_score, 1.0);
}
#[test]
fn test_variable_type_feedback_default() {
let feedback = VariableTypeFeedback::default();
assert!(feedback.assigned_types.is_empty());
}
#[test]
fn test_variable_type_feedback_is_stable() {
let mut feedback = VariableTypeFeedback::new();
feedback.stability_score = 0.9;
assert!(feedback.is_stable());
feedback.stability_score = 0.5;
assert!(!feedback.is_stable());
}
#[test]
fn test_call_site_feedback_new() {
let feedback = CallSiteFeedback::new();
assert!(feedback.arg_type_patterns.is_empty());
assert!(feedback.return_types.is_empty());
assert_eq!(feedback.call_count, 0);
}
#[test]
fn test_call_site_feedback_default() {
let feedback = CallSiteFeedback::default();
assert_eq!(feedback.call_count, 0);
}
#[test]
fn test_call_site_feedback_is_monomorphic_empty() {
let feedback = CallSiteFeedback::new();
assert!(!feedback.is_monomorphic());
}
#[test]
fn test_type_feedback_new() {
let feedback = TypeFeedback::new();
assert_eq!(feedback.total_samples(), 0);
}
#[test]
fn test_type_feedback_default() {
let feedback = TypeFeedback::default();
assert_eq!(feedback.total_samples(), 0);
}
#[test]
fn test_type_feedback_record_binary_op() {
let mut feedback = TypeFeedback::new();
let left = Value::Integer(1);
let right = Value::Integer(2);
let result = Value::Integer(3);
feedback.record_binary_op(0, &left, &right, &result);
assert_eq!(feedback.total_samples(), 1);
let stats = feedback.get_statistics();
assert_eq!(stats.total_operation_sites, 1);
}
#[test]
fn test_type_feedback_record_binary_op_monomorphic() {
let mut feedback = TypeFeedback::new();
for _ in 0..15 {
feedback.record_binary_op(
0,
&Value::Integer(1),
&Value::Integer(2),
&Value::Integer(3),
);
}
let stats = feedback.get_statistics();
assert_eq!(stats.monomorphic_operation_sites, 1);
}
#[test]
fn test_type_feedback_record_variable_assignment() {
let mut feedback = TypeFeedback::new();
let type_id = Value::Integer(1).type_id();
feedback.record_variable_assignment("x", type_id);
let stats = feedback.get_statistics();
assert_eq!(stats.total_variables, 1);
assert_eq!(stats.stable_variables, 1);
}
#[test]
fn test_type_feedback_variable_type_transition() {
let mut feedback = TypeFeedback::new();
let int_type = Value::Integer(1).type_id();
let float_type = Value::Float(1.0).type_id();
feedback.record_variable_assignment("x", int_type);
feedback.record_variable_assignment("x", float_type);
let stats = feedback.get_statistics();
assert_eq!(stats.total_variables, 1);
assert_eq!(stats.stable_variables, 0);
}
#[test]
fn test_type_feedback_record_function_call() {
let mut feedback = TypeFeedback::new();
let args = vec![Value::Integer(1)];
let result = Value::Integer(2);
feedback.record_function_call(0, "add", &args, &result);
let stats = feedback.get_statistics();
assert_eq!(stats.total_call_sites, 1);
}
#[test]
fn test_type_feedback_monomorphic_call_site() {
let mut feedback = TypeFeedback::new();
let args = vec![Value::Integer(1)];
let result = Value::Integer(2);
for _ in 0..10 {
feedback.record_function_call(0, "add", &args, &result);
}
let stats = feedback.get_statistics();
assert_eq!(stats.monomorphic_call_sites, 1);
}
#[test]
fn test_type_feedback_get_specialization_candidates_empty() {
let feedback = TypeFeedback::new();
let candidates = feedback.get_specialization_candidates();
assert!(candidates.is_empty());
}
#[test]
fn test_type_feedback_get_specialization_candidates() {
let mut feedback = TypeFeedback::new();
for _ in 0..15 {
feedback.record_binary_op(
0,
&Value::Integer(1),
&Value::Integer(2),
&Value::Integer(3),
);
}
let type_id = Value::Integer(1).type_id();
feedback.record_variable_assignment("x", type_id);
let args = vec![Value::Integer(1)];
for _ in 0..10 {
feedback.record_function_call(1, "func", &args, &Value::Integer(2));
}
let candidates = feedback.get_specialization_candidates();
assert!(!candidates.is_empty());
for i in 1..candidates.len() {
assert!(candidates[i - 1].benefit_score >= candidates[i].benefit_score);
}
}
#[test]
fn test_type_feedback_statistics() {
let mut feedback = TypeFeedback::new();
feedback.record_binary_op(
0,
&Value::Integer(1),
&Value::Integer(2),
&Value::Integer(3),
);
feedback.record_variable_assignment("x", Value::Integer(1).type_id());
feedback.record_function_call(1, "f", &[], &Value::Nil);
let stats = feedback.get_statistics();
assert_eq!(stats.total_operation_sites, 1);
assert_eq!(stats.total_variables, 1);
assert_eq!(stats.total_call_sites, 1);
assert_eq!(stats.total_samples, 1);
}
#[test]
fn test_specialization_kind_binary_op() {
let kind = SpecializationKind::BinaryOperation {
site_id: 0,
left_type: Value::Integer(1).type_id(),
right_type: Value::Integer(2).type_id(),
};
let _ = format!("{kind:?}");
}
#[test]
fn test_specialization_kind_variable() {
let kind = SpecializationKind::Variable {
name: "x".to_string(),
specialized_type: Value::Integer(1).type_id(),
};
let _ = format!("{kind:?}");
}
#[test]
fn test_specialization_kind_function_call() {
let kind = SpecializationKind::FunctionCall {
site_id: 0,
arg_types: vec![Value::Integer(1).type_id()],
return_type: Value::Integer(2).type_id(),
};
let _ = format!("{kind:?}");
}
#[test]
fn test_specialization_candidate() {
let candidate = SpecializationCandidate {
kind: SpecializationKind::Variable {
name: "x".to_string(),
specialized_type: Value::Integer(1).type_id(),
},
confidence: 0.95,
benefit_score: 100.0,
};
assert_eq!(candidate.confidence, 0.95);
assert_eq!(candidate.benefit_score, 100.0);
}
#[test]
fn test_type_feedback_stats() {
let stats = TypeFeedbackStats {
total_operation_sites: 10,
monomorphic_operation_sites: 5,
total_variables: 20,
stable_variables: 15,
total_call_sites: 8,
monomorphic_call_sites: 4,
total_samples: 100,
};
assert_eq!(stats.total_operation_sites, 10);
assert_eq!(stats.monomorphic_operation_sites, 5);
assert_eq!(stats.total_variables, 20);
assert_eq!(stats.stable_variables, 15);
assert_eq!(stats.total_call_sites, 8);
assert_eq!(stats.monomorphic_call_sites, 4);
assert_eq!(stats.total_samples, 100);
}
#[test]
fn test_inline_cache_multiple_hits_tracking() {
let mut cache = InlineCache::new();
let obj = Value::Integer(1);
cache.insert(&obj, "x".to_string(), Value::Integer(10));
for _ in 0..10 {
let result = cache.lookup(&obj, "x");
assert_eq!(result, Some(Value::Integer(10)));
}
assert_eq!(cache.total_hits(), 10);
}
#[test]
fn test_inline_cache_same_obj_different_fields() {
let mut cache = InlineCache::new();
let obj = Value::Integer(1);
cache.insert(&obj, "a".to_string(), Value::Integer(1));
cache.insert(&obj, "b".to_string(), Value::Integer(2));
assert_eq!(cache.lookup(&obj, "a"), Some(Value::Integer(1)));
assert_eq!(cache.lookup(&obj, "b"), Some(Value::Integer(2)));
}
#[test]
fn test_inline_cache_polymorphic_three_entries() {
let mut cache = InlineCache::new();
cache.insert(&Value::Integer(1), "a".to_string(), Value::Integer(1));
cache.insert(&Value::Float(1.0), "b".to_string(), Value::Float(2.0));
cache.insert(&Value::Bool(true), "c".to_string(), Value::Bool(false));
assert_eq!(*cache.state(), CacheState::Polymorphic);
assert_eq!(cache.entry_count(), 3);
}
#[test]
fn test_inline_cache_polymorphic_four_entries() {
let mut cache = InlineCache::new();
cache.insert(&Value::Integer(1), "a".to_string(), Value::Integer(1));
cache.insert(&Value::Float(1.0), "b".to_string(), Value::Float(2.0));
cache.insert(&Value::Bool(true), "c".to_string(), Value::Bool(false));
cache.insert(&Value::Nil, "d".to_string(), Value::Nil);
assert_eq!(*cache.state(), CacheState::Polymorphic);
assert_eq!(cache.entry_count(), 4);
}
#[test]
fn test_type_feedback_binary_op_multiple_sites() {
let mut feedback = TypeFeedback::new();
feedback.record_binary_op(
0,
&Value::Integer(1),
&Value::Integer(2),
&Value::Integer(3),
);
feedback.record_binary_op(
1,
&Value::Float(1.0),
&Value::Float(2.0),
&Value::Float(3.0),
);
let stats = feedback.get_statistics();
assert_eq!(stats.total_operation_sites, 2);
}
#[test]
fn test_type_feedback_binary_op_polymorphic_site() {
let mut feedback = TypeFeedback::new();
feedback.record_binary_op(
0,
&Value::Integer(1),
&Value::Integer(2),
&Value::Integer(3),
);
feedback.record_binary_op(
0,
&Value::Float(1.0),
&Value::Float(2.0),
&Value::Float(3.0),
);
let stats = feedback.get_statistics();
assert_eq!(stats.total_operation_sites, 1);
assert_eq!(stats.monomorphic_operation_sites, 0); }
#[test]
fn test_type_feedback_variable_multiple_vars() {
let mut feedback = TypeFeedback::new();
feedback.record_variable_assignment("x", Value::Integer(1).type_id());
feedback.record_variable_assignment("y", Value::Float(1.0).type_id());
feedback.record_variable_assignment("z", Value::Bool(true).type_id());
let stats = feedback.get_statistics();
assert_eq!(stats.total_variables, 3);
assert_eq!(stats.stable_variables, 3); }
#[test]
fn test_type_feedback_variable_multiple_transitions() {
let mut feedback = TypeFeedback::new();
feedback.record_variable_assignment("x", Value::Integer(1).type_id());
feedback.record_variable_assignment("x", Value::Float(1.0).type_id());
feedback.record_variable_assignment("x", Value::Bool(true).type_id());
let stats = feedback.get_statistics();
assert_eq!(stats.total_variables, 1);
assert_eq!(stats.stable_variables, 0); }
#[test]
fn test_type_feedback_call_site_multiple_calls() {
let mut feedback = TypeFeedback::new();
let args1 = vec![Value::Integer(1)];
let args2 = vec![Value::Float(1.0)];
feedback.record_function_call(0, "func", &args1, &Value::Integer(2));
feedback.record_function_call(0, "func", &args2, &Value::Float(2.0));
let stats = feedback.get_statistics();
assert_eq!(stats.total_call_sites, 1);
assert_eq!(stats.monomorphic_call_sites, 0); }
#[test]
fn test_type_feedback_call_site_same_pattern_repeated() {
let mut feedback = TypeFeedback::new();
let args = vec![Value::Integer(1)];
for _ in 0..10 {
feedback.record_function_call(0, "func", &args, &Value::Integer(2));
}
let stats = feedback.get_statistics();
assert_eq!(stats.total_call_sites, 1);
assert_eq!(stats.monomorphic_call_sites, 1);
}
#[test]
fn test_type_feedback_specialization_not_enough_samples() {
let mut feedback = TypeFeedback::new();
for _ in 0..5 {
feedback.record_binary_op(
0,
&Value::Integer(1),
&Value::Integer(2),
&Value::Integer(3),
);
}
let candidates = feedback.get_specialization_candidates();
let has_binary_op = candidates
.iter()
.any(|c| matches!(c.kind, SpecializationKind::BinaryOperation { .. }));
assert!(!has_binary_op);
}
#[test]
fn test_type_feedback_total_samples_tracking() {
let mut feedback = TypeFeedback::new();
for i in 0..20 {
feedback.record_binary_op(
i % 5,
&Value::Integer(1),
&Value::Integer(2),
&Value::Integer(3),
);
}
assert_eq!(feedback.total_samples(), 20);
}
#[test]
fn test_inline_cache_hit_rate_calculation() {
let mut cache = InlineCache::new();
let obj = Value::Integer(1);
cache.insert(&obj, "x".to_string(), Value::Integer(10));
cache.lookup(&obj, "x");
cache.lookup(&obj, "x");
cache.lookup(&obj, "x");
cache.lookup(&obj, "y");
assert!((cache.hit_rate() - 0.75).abs() < 0.01);
}
#[test]
fn test_specialization_candidate_sorting() {
let mut feedback = TypeFeedback::new();
for _ in 0..15 {
feedback.record_binary_op(
0,
&Value::Integer(1),
&Value::Integer(2),
&Value::Integer(3),
);
}
let args = vec![Value::Integer(1)];
for _ in 0..10 {
feedback.record_function_call(1, "func", &args, &Value::Integer(2));
}
let candidates = feedback.get_specialization_candidates();
assert!(candidates.len() >= 2);
for i in 1..candidates.len() {
assert!(candidates[i - 1].benefit_score >= candidates[i].benefit_score);
}
}
#[test]
fn test_cache_entry_debug_format() {
let entry = CacheEntry {
type_id: Value::Integer(1).type_id(),
field_name: "test".to_string(),
cached_result: Value::Bool(true),
hit_count: 5,
};
let debug = format!("{entry:?}");
assert!(debug.contains("test"));
assert!(debug.contains("5"));
}
#[test]
fn test_cache_state_debug_format() {
let states = [
CacheState::Uninitialized,
CacheState::Monomorphic,
CacheState::Polymorphic,
CacheState::Megamorphic,
];
for state in states {
let debug = format!("{state:?}");
assert!(!debug.is_empty());
}
}
#[test]
fn test_type_feedback_stats_debug() {
let stats = TypeFeedbackStats {
total_operation_sites: 10,
monomorphic_operation_sites: 5,
total_variables: 20,
stable_variables: 15,
total_call_sites: 8,
monomorphic_call_sites: 4,
total_samples: 100,
};
let debug = format!("{stats:?}");
assert!(debug.contains("10"));
assert!(debug.contains("100"));
}
#[test]
fn test_inline_cache_eviction_policy() {
let mut cache = InlineCache::new();
cache.insert(&Value::Integer(1), "a".to_string(), Value::Integer(1));
cache.insert(&Value::Float(1.0), "b".to_string(), Value::Float(2.0));
cache.insert(&Value::Bool(true), "c".to_string(), Value::Bool(false));
cache.insert(&Value::Nil, "d".to_string(), Value::Nil);
for _ in 0..10 {
cache.lookup(&Value::Integer(1), "a");
}
cache.insert(
&Value::from_string("test".to_string()),
"e".to_string(),
Value::from_string("val".to_string()),
);
assert_eq!(*cache.state(), CacheState::Megamorphic);
assert!(cache.lookup(&Value::Integer(1), "a").is_some());
}
#[test]
fn test_variable_stability_score_calculation() {
let mut feedback = TypeFeedback::new();
feedback.record_variable_assignment("x", Value::Integer(1).type_id());
let stats = feedback.get_statistics();
assert_eq!(stats.stable_variables, 1);
feedback.record_variable_assignment("x", Value::Float(1.0).type_id());
let stats = feedback.get_statistics();
assert_eq!(stats.stable_variables, 0);
}
#[test]
fn test_operation_feedback_type_counts() {
let mut feedback = TypeFeedback::new();
for _ in 0..3 {
feedback.record_binary_op(
0,
&Value::Integer(1),
&Value::Integer(2),
&Value::Integer(3),
);
}
for _ in 0..2 {
feedback.record_binary_op(
0,
&Value::Float(1.0),
&Value::Float(2.0),
&Value::Float(3.0),
);
}
assert_eq!(feedback.total_samples(), 5);
}
#[test]
fn test_specialization_candidate_clone() {
let candidate = SpecializationCandidate {
kind: SpecializationKind::Variable {
name: "x".to_string(),
specialized_type: Value::Integer(1).type_id(),
},
confidence: 0.95,
benefit_score: 100.0,
};
let cloned = candidate.clone();
assert_eq!(cloned.confidence, candidate.confidence);
assert_eq!(cloned.benefit_score, candidate.benefit_score);
}
}