Add into_rc_any and push_value to Object
This commit introduces the `into_rc_any` and `push_value` methods to the `Object` trait. The `into_rc_any` method allows for efficient, zero-cost downcasting of `Rc<Self>` to `Rc<dyn Any>`. This is a crucial step for enabling more flexible dynamic dispatch and type manipulation within the VM. The `push_value` method is implemented for series types (`ScalarSeries`, `ValueSeries`, `RecordSeries`) to allow pushing values directly onto them. This simplifies the `push` intrinsic function, removing the need for manual downcasting within its implementation. The default implementation for non-series objects panics, enforcing that only mutable series support this operation. Additionally, `ScalarValue` now includes a `from_value` method to facilitate converting `Value` back into concrete scalar types. This improves type safety and reduces boilerplate code when handling scalar conversions.
This commit is contained in:
@@ -276,6 +276,9 @@ impl Object for Node<SyntaxPhase> {
|
|||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type TypedNode = Node<TypedPhase>;
|
pub type TypedNode = Node<TypedPhase>;
|
||||||
|
|||||||
+60
-59
@@ -77,22 +77,33 @@ impl<T> RingBuffer<T> {
|
|||||||
/// (No pointers/heaps like String or Record).
|
/// (No pointers/heaps like String or Record).
|
||||||
pub trait ScalarValue: Copy + Debug + 'static {
|
pub trait ScalarValue: Copy + Debug + 'static {
|
||||||
fn to_value(self) -> Value;
|
fn to_value(self) -> Value;
|
||||||
|
/// Converts a dynamic `Value` back to this scalar type, returning `None` on mismatch.
|
||||||
|
fn from_value(v: Value) -> Option<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScalarValue for f64 {
|
impl ScalarValue for f64 {
|
||||||
fn to_value(self) -> Value {
|
fn to_value(self) -> Value {
|
||||||
Value::Float(self)
|
Value::Float(self)
|
||||||
}
|
}
|
||||||
|
fn from_value(v: Value) -> Option<Self> {
|
||||||
|
v.as_float()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl ScalarValue for i64 {
|
impl ScalarValue for i64 {
|
||||||
fn to_value(self) -> Value {
|
fn to_value(self) -> Value {
|
||||||
Value::Int(self)
|
Value::Int(self)
|
||||||
}
|
}
|
||||||
|
fn from_value(v: Value) -> Option<Self> {
|
||||||
|
v.as_int()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl ScalarValue for bool {
|
impl ScalarValue for bool {
|
||||||
fn to_value(self) -> Value {
|
fn to_value(self) -> Value {
|
||||||
Value::Bool(self)
|
Value::Bool(self)
|
||||||
}
|
}
|
||||||
|
fn from_value(v: Value) -> Option<Self> {
|
||||||
|
if let Value::Bool(b) = v { Some(b) } else { None }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A highly optimized, type-safe series for primitive values (Float, Int, Bool).
|
/// A highly optimized, type-safe series for primitive values (Float, Int, Bool).
|
||||||
@@ -130,6 +141,14 @@ impl<T: ScalarValue> Object for ScalarSeries<T> {
|
|||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn push_value(&self, value: Value) {
|
||||||
|
if let Some(v) = T::from_value(value) {
|
||||||
|
self.data.borrow_mut().push(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
fn as_series(&self) -> Option<&dyn Series> {
|
fn as_series(&self) -> Option<&dyn Series> {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
@@ -178,6 +197,12 @@ impl Object for ValueSeries {
|
|||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn push_value(&self, value: Value) {
|
||||||
|
self.data.borrow_mut().push(value);
|
||||||
|
}
|
||||||
fn as_series(&self) -> Option<&dyn Series> {
|
fn as_series(&self) -> Option<&dyn Series> {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
@@ -199,42 +224,15 @@ impl Series for ValueSeries {
|
|||||||
// 3. RecordSeries (SoA - Struct of Arrays)
|
// 3. RecordSeries (SoA - Struct of Arrays)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
/// An interface allowing iteration over fields of a RecordSeries independently
|
/// Marker supertrait combining `Object` and `Series` for RecordSeries field buffers.
|
||||||
/// of the exact type (Type Erasure for member arrays).
|
/// Ensures each field buffer can be stored as a typed series AND used as a dynamic object
|
||||||
pub trait SeriesMember: Object + Series {
|
/// (including `push_value` via the `Object` supertrait).
|
||||||
/// Pushes a dynamic Value into the series, performing the necessary downcast.
|
pub trait SeriesMember: Object + Series {}
|
||||||
fn push_value(&self, value: Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SeriesMember for ScalarSeries<f64> {
|
impl SeriesMember for ScalarSeries<f64> {}
|
||||||
fn push_value(&self, value: Value) {
|
impl SeriesMember for ScalarSeries<i64> {}
|
||||||
if let Some(v) = value.as_float() {
|
impl SeriesMember for ScalarSeries<bool> {}
|
||||||
self.data.borrow_mut().push(v);
|
impl SeriesMember for ValueSeries {}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SeriesMember for ScalarSeries<i64> {
|
|
||||||
fn push_value(&self, value: Value) {
|
|
||||||
if let Some(v) = value.as_int() {
|
|
||||||
self.data.borrow_mut().push(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SeriesMember for ScalarSeries<bool> {
|
|
||||||
fn push_value(&self, value: Value) {
|
|
||||||
if let Value::Bool(v) = value {
|
|
||||||
self.data.borrow_mut().push(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SeriesMember for ValueSeries {
|
|
||||||
fn push_value(&self, value: Value) {
|
|
||||||
self.data.borrow_mut().push(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The "Struct of Arrays" implementation for Records (e.g., Ticks).
|
/// The "Struct of Arrays" implementation for Records (e.g., Ticks).
|
||||||
/// Instead of holding a large array of Records (AoS), this series
|
/// Instead of holding a large array of Records (AoS), this series
|
||||||
@@ -320,6 +318,20 @@ impl Object for RecordSeries {
|
|||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn push_value(&self, value: Value) {
|
||||||
|
if let Value::Record(_, values) = value {
|
||||||
|
for (i, v) in values.iter().enumerate() {
|
||||||
|
if let Some(f) = self.fields.get(i) {
|
||||||
|
f.borrow().push_value(v.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("push to RecordSeries expects a record");
|
||||||
|
}
|
||||||
|
}
|
||||||
fn as_series(&self) -> Option<&dyn Series> {
|
fn as_series(&self) -> Option<&dyn Series> {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
@@ -374,6 +386,9 @@ impl Object for SeriesView {
|
|||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
|
||||||
|
self
|
||||||
|
}
|
||||||
fn as_series(&self) -> Option<&dyn Series> {
|
fn as_series(&self) -> Option<&dyn Series> {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
@@ -420,6 +435,9 @@ impl<T: ScalarValue> Object for SharedSeries<T> {
|
|||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
|
||||||
|
self
|
||||||
|
}
|
||||||
fn as_series(&self) -> Option<&dyn Series> {
|
fn as_series(&self) -> Option<&dyn Series> {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
@@ -458,6 +476,9 @@ impl Object for SharedValueSeries {
|
|||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
|
||||||
|
self
|
||||||
|
}
|
||||||
fn as_series(&self) -> Option<&dyn Series> {
|
fn as_series(&self) -> Option<&dyn Series> {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
@@ -499,6 +520,9 @@ impl Object for SharedRecordSeries {
|
|||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
|
||||||
|
self
|
||||||
|
}
|
||||||
fn as_series(&self) -> Option<&dyn Series> {
|
fn as_series(&self) -> Option<&dyn Series> {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
@@ -598,30 +622,7 @@ pub fn register(env: &Environment) {
|
|||||||
let (series_val, val) = (&args[0], &args[1]);
|
let (series_val, val) = (&args[0], &args[1]);
|
||||||
|
|
||||||
if let Value::Object(obj) = series_val {
|
if let Value::Object(obj) = series_val {
|
||||||
let any = obj.as_any();
|
obj.push_value(val.clone());
|
||||||
|
|
||||||
// Polymorphic push logic
|
|
||||||
if let Some(rs) = any.downcast_ref::<RecordSeries>() {
|
|
||||||
if let Value::Record(_, values) = val {
|
|
||||||
for (i, v) in values.iter().enumerate() {
|
|
||||||
if let Some(f) = rs.fields.get(i) {
|
|
||||||
f.borrow().push_value(v.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
panic!("push to RecordSeries expects a record");
|
|
||||||
}
|
|
||||||
} else if let Some(s) = any.downcast_ref::<ScalarSeries<f64>>() {
|
|
||||||
s.push_value(val.clone());
|
|
||||||
} else if let Some(s) = any.downcast_ref::<ScalarSeries<i64>>() {
|
|
||||||
s.push_value(val.clone());
|
|
||||||
} else if let Some(s) = any.downcast_ref::<ScalarSeries<bool>>() {
|
|
||||||
s.push_value(val.clone());
|
|
||||||
} else if let Some(s) = any.downcast_ref::<ValueSeries>() {
|
|
||||||
s.push_value(val.clone());
|
|
||||||
} else {
|
|
||||||
panic!("Object is not a mutable series");
|
|
||||||
}
|
|
||||||
return Value::Void;
|
return Value::Void;
|
||||||
}
|
}
|
||||||
panic!("push expects a series object as the first argument")
|
panic!("push expects a series object as the first argument")
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ impl Object for StreamNode {
|
|||||||
fn as_any(&self) -> &dyn std::any::Any {
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
fn into_rc_any(self: std::rc::Rc<Self>) -> std::rc::Rc<dyn std::any::Any> {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The RootStream is the "Clock" and data source of the entire pipeline.
|
/// The RootStream is the "Clock" and data source of the entire pipeline.
|
||||||
|
|||||||
@@ -180,6 +180,16 @@ pub trait Object: fmt::Debug {
|
|||||||
fn type_name(&self) -> &'static str;
|
fn type_name(&self) -> &'static str;
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
|
|
||||||
|
/// Converts `Rc<Self>` into `Rc<dyn Any>`, enabling zero-copy downcasting to a concrete
|
||||||
|
/// `Rc<T>` via `Rc::downcast::<T>()`. Requires `arbitrary_self_types` (stable since 1.81).
|
||||||
|
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any>;
|
||||||
|
|
||||||
|
/// Pushes a value into this object if it is a mutable series.
|
||||||
|
/// The default implementation panics — only series types override this.
|
||||||
|
fn push_value(&self, _val: Value) {
|
||||||
|
panic!("{} does not support push_value", self.type_name());
|
||||||
|
}
|
||||||
|
|
||||||
/// Optional optimization: If this object behaves like a time series (indexable lookbacks),
|
/// Optional optimization: If this object behaves like a time series (indexable lookbacks),
|
||||||
/// it can return a reference to its Series trait implementation.
|
/// it can return a reference to its Series trait implementation.
|
||||||
fn as_series(&self) -> Option<&dyn Series> {
|
fn as_series(&self) -> Option<&dyn Series> {
|
||||||
|
|||||||
+21
-16
@@ -48,12 +48,15 @@ impl Object for Closure {
|
|||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct CallFrame {
|
struct CallFrame {
|
||||||
stack_base: usize,
|
stack_base: usize,
|
||||||
closure: Option<Rc<dyn Object>>,
|
closure: Option<Rc<Closure>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait VMObserver {
|
pub trait VMObserver {
|
||||||
@@ -154,13 +157,14 @@ impl VM {
|
|||||||
match result {
|
match result {
|
||||||
Ok(Value::TailCallRequest(payload)) => {
|
Ok(Value::TailCallRequest(payload)) => {
|
||||||
let (next_obj, next_args) = *payload;
|
let (next_obj, next_args) = *payload;
|
||||||
if let Some(closure) = next_obj.as_any().downcast_ref::<Closure>() {
|
if let Ok(closure_rc) = next_obj.clone().into_rc_any().downcast::<Closure>() {
|
||||||
self.stack.clear();
|
self.stack.clear();
|
||||||
// frames should be empty here since we popped before entering the loop
|
// frames should be empty here since we popped before entering the loop
|
||||||
self.frames.push(CallFrame {
|
self.frames.push(CallFrame {
|
||||||
stack_base: 0,
|
stack_base: 0,
|
||||||
closure: Some(next_obj.clone()),
|
closure: Some(closure_rc.clone()),
|
||||||
});
|
});
|
||||||
|
let closure = closure_rc.as_ref();
|
||||||
if let Some(count) = closure.positional_count
|
if let Some(count) = closure.positional_count
|
||||||
&& next_args.len() == count as usize
|
&& next_args.len() == count as usize
|
||||||
{
|
{
|
||||||
@@ -227,14 +231,15 @@ impl VM {
|
|||||||
closure_obj: Rc<dyn Object>,
|
closure_obj: Rc<dyn Object>,
|
||||||
args: &[Value],
|
args: &[Value],
|
||||||
) -> Result<Value, String> {
|
) -> Result<Value, String> {
|
||||||
let closure = closure_obj
|
let closure_rc = closure_obj
|
||||||
.as_any()
|
.into_rc_any()
|
||||||
.downcast_ref::<Closure>()
|
.downcast::<Closure>()
|
||||||
.ok_or_else(|| "Object is not a closure".to_string())?;
|
.map_err(|_| "Object is not a closure".to_string())?;
|
||||||
|
|
||||||
self.stack.clear();
|
self.stack.clear();
|
||||||
self.frames.clear();
|
self.frames.clear();
|
||||||
|
|
||||||
|
let closure = closure_rc.as_ref();
|
||||||
if let Some(count) = closure.positional_count
|
if let Some(count) = closure.positional_count
|
||||||
&& args.len() == count as usize
|
&& args.len() == count as usize
|
||||||
{
|
{
|
||||||
@@ -251,7 +256,7 @@ impl VM {
|
|||||||
|
|
||||||
self.frames.push(CallFrame {
|
self.frames.push(CallFrame {
|
||||||
stack_base: 0,
|
stack_base: 0,
|
||||||
closure: Some(closure_obj.clone()),
|
closure: Some(closure_rc.clone()),
|
||||||
});
|
});
|
||||||
|
|
||||||
let result = self.eval_internal(observer, &closure.exec_node);
|
let result = self.eval_internal(observer, &closure.exec_node);
|
||||||
@@ -652,11 +657,14 @@ impl VM {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
Value::Object(obj) => {
|
Value::Object(obj) => {
|
||||||
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
|
if let Ok(closure_rc) =
|
||||||
|
obj.clone().into_rc_any().downcast::<Closure>()
|
||||||
|
{
|
||||||
self.frames.push(CallFrame {
|
self.frames.push(CallFrame {
|
||||||
stack_base: base,
|
stack_base: base,
|
||||||
closure: Some(obj.clone()),
|
closure: Some(closure_rc.clone()),
|
||||||
});
|
});
|
||||||
|
let closure = closure_rc.as_ref();
|
||||||
|
|
||||||
let unpack_res = if let Some(count) = closure.positional_count
|
let unpack_res = if let Some(count) = closure.positional_count
|
||||||
&& (self.stack.len() - base) == count as usize
|
&& (self.stack.len() - base) == count as usize
|
||||||
@@ -915,8 +923,7 @@ impl VM {
|
|||||||
}
|
}
|
||||||
Address::Upvalue(idx) => {
|
Address::Upvalue(idx) => {
|
||||||
let frame = self.frames.last().ok_or("No call frame")?;
|
let frame = self.frames.last().ok_or("No call frame")?;
|
||||||
if let Some(closure_obj) = &frame.closure {
|
if let Some(closure) = &frame.closure {
|
||||||
let closure = closure_obj.as_any().downcast_ref::<Closure>().unwrap();
|
|
||||||
let u_idx = idx.0 as usize;
|
let u_idx = idx.0 as usize;
|
||||||
if u_idx < closure.upvalues.len() {
|
if u_idx < closure.upvalues.len() {
|
||||||
Ok(closure.upvalues[u_idx].clone())
|
Ok(closure.upvalues[u_idx].clone())
|
||||||
@@ -956,8 +963,7 @@ impl VM {
|
|||||||
}
|
}
|
||||||
Address::Upvalue(idx) => {
|
Address::Upvalue(idx) => {
|
||||||
let frame = self.frames.last().ok_or("No call frame")?;
|
let frame = self.frames.last().ok_or("No call frame")?;
|
||||||
if let Some(closure_obj) = &frame.closure {
|
if let Some(closure) = &frame.closure {
|
||||||
let closure = closure_obj.as_any().downcast_ref::<Closure>().unwrap();
|
|
||||||
let u_idx = idx.0 as usize;
|
let u_idx = idx.0 as usize;
|
||||||
if u_idx < closure.upvalues.len() {
|
if u_idx < closure.upvalues.len() {
|
||||||
Ok(closure.upvalues[u_idx].borrow().clone())
|
Ok(closure.upvalues[u_idx].borrow().clone())
|
||||||
@@ -1000,8 +1006,7 @@ impl VM {
|
|||||||
}
|
}
|
||||||
Address::Upvalue(idx) => {
|
Address::Upvalue(idx) => {
|
||||||
let frame = self.frames.last().ok_or("No call frame")?;
|
let frame = self.frames.last().ok_or("No call frame")?;
|
||||||
if let Some(closure_obj) = &frame.closure {
|
if let Some(closure) = &frame.closure {
|
||||||
let closure = closure_obj.as_any().downcast_ref::<Closure>().unwrap();
|
|
||||||
let u_idx = idx.0 as usize;
|
let u_idx = idx.0 as usize;
|
||||||
if u_idx < closure.upvalues.len() {
|
if u_idx < closure.upvalues.len() {
|
||||||
*closure.upvalues[u_idx].borrow_mut() = value;
|
*closure.upvalues[u_idx].borrow_mut() = value;
|
||||||
|
|||||||
Reference in New Issue
Block a user