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::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::optimizer::Optimizer;
|
||||
use crate::ast::compiler::specializer::{FunctionRegistry, MonoCache, Specializer};
|
||||
|
||||
+6
-28
@@ -335,7 +335,7 @@ impl VM {
|
||||
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.
|
||||
Ok(val)
|
||||
}
|
||||
@@ -351,18 +351,14 @@ impl VM {
|
||||
}
|
||||
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 } => {
|
||||
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 {
|
||||
// Case 1: The classic Record.
|
||||
// This is a struct-like tuple containing an Arc<RecordLayout> and a Vec<Value>.
|
||||
Value::Record(layout, values) => {
|
||||
if let Some(idx) = layout.index_of(*field) {
|
||||
Ok(values[idx].clone())
|
||||
@@ -370,28 +366,14 @@ impl VM {
|
||||
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) => {
|
||||
// 1. We get the raw `&dyn Any` reference (Rust's standard mechanism for runtime type reflection).
|
||||
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) =
|
||||
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) {
|
||||
// 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 =
|
||||
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)));
|
||||
} else {
|
||||
return Err(format!(
|
||||
@@ -400,18 +382,14 @@ impl VM {
|
||||
));
|
||||
}
|
||||
} 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)));
|
||||
}
|
||||
|
||||
// Fallback if it's another type of object that is not a RecordSeries.
|
||||
Err(format!(
|
||||
"Attempt to access field on non-record object: {}",
|
||||
obj.type_name()
|
||||
))
|
||||
}
|
||||
|
||||
// Error handling for primitives (Int, Float, etc.).
|
||||
_ => Err(format!(
|
||||
"Attempt to access field on non-record: {}",
|
||||
rec_val
|
||||
@@ -421,7 +399,7 @@ impl VM {
|
||||
|
||||
BoundKind::Set { addr, value } => {
|
||||
let val = self.eval_internal(obs, value)?;
|
||||
self.set_value(*addr, val.clone())?;
|
||||
self.set_value(addr.clone(), val.clone())?;
|
||||
Ok(val)
|
||||
}
|
||||
BoundKind::If {
|
||||
|
||||
Reference in New Issue
Block a user