use std::any::Any;
use std::collections::HashMap;
use std::cell::{Cell, RefCell};
use object::Object;
use object_pool::ObjectPool;
use value::{Value, ValueContext};
use executor::ExecutorImpl;
use errors::{VMError, RuntimeError};
pub struct DynamicObject {
prototype: Option<usize>,
fields: RefCell<HashMap<String, Value>>,
frozen: Cell<bool>
}
impl Object for DynamicObject {
fn get_children(&self) -> Vec<usize> {
let mut children: Vec<usize> = self.fields.borrow().iter().map(|(_, v)| {
if let Value::Object(id) = *v {
Some(id)
} else {
None
}
}).filter(|v| if v.is_some() { true } else { false }).map(|v| v.unwrap()).collect();
if let Some(prototype) = self.prototype {
children.push(prototype);
}
children
}
fn as_any(&self) -> &Any {
self as &Any
}
fn as_any_mut(&mut self) -> &mut Any {
self as &mut Any
}
fn get_field(&self, pool: &ObjectPool, name: &str) -> Option<Value> {
if let Some(v) = self.fields.borrow().get(name) {
Some(*v)
} else {
if let Some(prototype) = self.prototype {
let pt_object = pool.get_direct(prototype);
pt_object.get_field(pool, name)
} else {
None
}
}
}
fn has_const_field(&self, pool: &ObjectPool, name: &str) -> bool {
if self.frozen.get() {
true
} else {
if let Some(prototype) = self.prototype {
let pt_object = pool.get_direct(prototype);
pt_object.has_const_field(pool, name)
} else {
false
}
}
}
fn set_field(&self, name: &str, value: Value) {
if self.frozen.get() {
panic!(VMError::from("Attempting to set field on a frozen dynamic object"));
}
self.fields.borrow_mut().insert(name.to_string(), value);
}
fn call(&self, executor: &mut ExecutorImpl) -> Value {
let target = match self.get_field(executor.get_object_pool(), "__call__") {
Some(v) => v,
None => panic!(VMError::from(RuntimeError::new(
"Attempting to call a dynamic object without the `__call__` method"
)))
};
let target = ValueContext::new(&target, executor.get_object_pool()).as_object();
target.call(executor)
}
}
impl DynamicObject {
pub fn new(prototype: Option<usize>) -> DynamicObject {
DynamicObject {
prototype: prototype,
fields: RefCell::new(HashMap::new()),
frozen: Cell::new(false)
}
}
pub fn freeze(&self) {
self.frozen.set(true);
}
}