Refactor VM field access to use Rc clones
The VM's field access logic has been refactored to clone `Rc` pointers for addresses and field identifiers. This change aims to improve efficiency by avoiding unnecessary dereferencing and copying of values when accessing fields within records and objects. Additionally, the `Value::Object` handling in `BoundKind::GetField` has been refined to more explicitly manage `RecordSeries` and `StreamNode` types, ensuring that field access is handled polymorphically and efficiently without copying underlying data structures.
This commit is contained in:
@@ -15,7 +15,7 @@ use crate::ast::compiler::bound_nodes::{
|
|||||||
};
|
};
|
||||||
use crate::ast::compiler::dumper::Dumper;
|
use crate::ast::compiler::dumper::Dumper;
|
||||||
use crate::ast::compiler::lambda_collector::LambdaCollector;
|
use crate::ast::compiler::lambda_collector::LambdaCollector;
|
||||||
use crate::ast::compiler::lowering::{ExecNode, Lowering};
|
use crate::ast::compiler::lowering::{Lowering, ExecNode};
|
||||||
use crate::ast::compiler::macros::{MacroEvaluator, MacroExpander, MacroRegistry};
|
use crate::ast::compiler::macros::{MacroEvaluator, MacroExpander, MacroRegistry};
|
||||||
use crate::ast::compiler::optimizer::Optimizer;
|
use crate::ast::compiler::optimizer::Optimizer;
|
||||||
use crate::ast::compiler::specializer::{FunctionRegistry, MonoCache, Specializer};
|
use crate::ast::compiler::specializer::{FunctionRegistry, MonoCache, Specializer};
|
||||||
|
|||||||
+6
-28
@@ -335,7 +335,7 @@ impl VM {
|
|||||||
val.clone()
|
val.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_value(*addr, store_val)?;
|
self.set_value(addr.clone(), store_val)?;
|
||||||
// Define always evaluates to the unwrapped value for immediate use.
|
// Define always evaluates to the unwrapped value for immediate use.
|
||||||
Ok(val)
|
Ok(val)
|
||||||
}
|
}
|
||||||
@@ -351,18 +351,14 @@ impl VM {
|
|||||||
}
|
}
|
||||||
Ok(val)
|
Ok(val)
|
||||||
}
|
}
|
||||||
BoundKind::Get { addr, .. } => self.get_value(*addr),
|
BoundKind::Get { addr, .. } => self.get_value(addr.clone()),
|
||||||
|
|
||||||
BoundKind::FieldAccessor(k) => Ok(Value::FieldAccessor(*k)),
|
BoundKind::FieldAccessor(k) => Ok(Value::FieldAccessor(k.clone())),
|
||||||
|
|
||||||
BoundKind::GetField { rec, field } => {
|
BoundKind::GetField { rec, field } => {
|
||||||
let rec_val = self.eval_internal(obs, rec)?;
|
let rec_val = self.eval_internal(obs, rec)?;
|
||||||
|
|
||||||
// In Rust, pattern matching (`match`) is the idiomatic way to handle variants safely.
|
|
||||||
// Previously, this only handled `Value::Record`. Now, we handle objects (like `RecordSeries`) polymorphically.
|
|
||||||
match rec_val {
|
match rec_val {
|
||||||
// Case 1: The classic Record.
|
|
||||||
// This is a struct-like tuple containing an Arc<RecordLayout> and a Vec<Value>.
|
|
||||||
Value::Record(layout, values) => {
|
Value::Record(layout, values) => {
|
||||||
if let Some(idx) = layout.index_of(*field) {
|
if let Some(idx) = layout.index_of(*field) {
|
||||||
Ok(values[idx].clone())
|
Ok(values[idx].clone())
|
||||||
@@ -370,28 +366,14 @@ impl VM {
|
|||||||
Err(format!("Record does not have field :{}", field.name()))
|
Err(format!("Record does not have field :{}", field.name()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 2: A dynamic Object (our SoA / Struct-of-Arrays optimization).
|
|
||||||
// `Value::Object` holds an `Rc<dyn Object>` - a reference-counted trait object (type-erased).
|
|
||||||
Value::Object(obj) => {
|
Value::Object(obj) => {
|
||||||
// 1. We get the raw `&dyn Any` reference (Rust's standard mechanism for runtime type reflection).
|
|
||||||
let any_ptr = obj.as_any();
|
let any_ptr = obj.as_any();
|
||||||
|
|
||||||
// 2. Downcast! We check at runtime if the pointer actually points to a `RecordSeries`.
|
|
||||||
// `downcast_ref` is very fast (essentially an O(1) type ID comparison under the hood).
|
|
||||||
if let Some(record_series) =
|
if let Some(record_series) =
|
||||||
any_ptr.downcast_ref::<crate::ast::rtl::series::RecordSeries>()
|
any_ptr.downcast_ref::<crate::ast::rtl::series::RecordSeries>()
|
||||||
{
|
{
|
||||||
// 3. We call our highly performant 0-copy method on the series.
|
|
||||||
// It returns an `Rc<RefCell<dyn SeriesMember>>`, which is a shared pointer
|
|
||||||
// to the concrete column array (e.g., a `ScalarSeries<f64>`).
|
|
||||||
if let Some(field_series) = record_series.field(*field) {
|
if let Some(field_series) = record_series.field(*field) {
|
||||||
// 4. We wrap this RefCell in our `SeriesView` struct.
|
|
||||||
// The `SeriesView` acts as a pure `Object` for the VM, holding the reference.
|
|
||||||
// CRITICAL: No array elements are copied here! This is pure, fast pointer juggling.
|
|
||||||
// This single operation turns a SoA `RecordSeries` into a high-speed `FloatSeries` view.
|
|
||||||
let view =
|
let view =
|
||||||
crate::ast::rtl::series::SeriesView::new(field_series, *field);
|
crate::ast::rtl::series::SeriesView::new(field_series, field.clone());
|
||||||
return Ok(Value::Object(std::rc::Rc::new(view)));
|
return Ok(Value::Object(std::rc::Rc::new(view)));
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
@@ -400,18 +382,14 @@ impl VM {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else if let Some(sn) = obj.as_any().downcast_ref::<crate::ast::rtl::streams::StreamNode>() {
|
} else if let Some(sn) = obj.as_any().downcast_ref::<crate::ast::rtl::streams::StreamNode>() {
|
||||||
let mapped = crate::ast::rtl::streams::build_map_stream(sn.inner.clone(), *field);
|
let mapped = crate::ast::rtl::streams::build_map_stream(sn.inner.clone(), field.clone());
|
||||||
return Ok(Value::Object(Rc::new(mapped)));
|
return Ok(Value::Object(Rc::new(mapped)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback if it's another type of object that is not a RecordSeries.
|
|
||||||
Err(format!(
|
Err(format!(
|
||||||
"Attempt to access field on non-record object: {}",
|
"Attempt to access field on non-record object: {}",
|
||||||
obj.type_name()
|
obj.type_name()
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error handling for primitives (Int, Float, etc.).
|
|
||||||
_ => Err(format!(
|
_ => Err(format!(
|
||||||
"Attempt to access field on non-record: {}",
|
"Attempt to access field on non-record: {}",
|
||||||
rec_val
|
rec_val
|
||||||
@@ -421,7 +399,7 @@ impl VM {
|
|||||||
|
|
||||||
BoundKind::Set { addr, value } => {
|
BoundKind::Set { addr, value } => {
|
||||||
let val = self.eval_internal(obs, value)?;
|
let val = self.eval_internal(obs, value)?;
|
||||||
self.set_value(*addr, val.clone())?;
|
self.set_value(addr.clone(), val.clone())?;
|
||||||
Ok(val)
|
Ok(val)
|
||||||
}
|
}
|
||||||
BoundKind::If {
|
BoundKind::If {
|
||||||
|
|||||||
Reference in New Issue
Block a user