blob: b44e94eb8337d27555380385fed342e28892e440 [file] [log] [blame]
use std::prelude::v1::*;
use std::any::Any;
use std::cell::RefCell;
use object::Object;
use object_pool::ObjectPool;
use basic_block::BasicBlock;
use executor::ExecutorImpl;
use errors;
use function_optimizer::FunctionOptimizer;
use value::Value;
pub enum Function {
Virtual(RefCell<VirtualFunction>),
Native(NativeFunction)
}
pub struct VirtualFunction {
basic_blocks: Vec<BasicBlock>,
rt_handles: Vec<usize>,
should_optimize: bool,
this: Option<Value>
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct VirtualFunctionInfo {
pub basic_blocks: Vec<BasicBlock>
}
pub type NativeFunction = Box<Fn(&mut ExecutorImpl) -> Value + Send>;
impl Object for Function {
fn initialize(&mut self, pool: &mut ObjectPool) {
self.static_optimize(pool);
}
fn get_children(&self) -> Vec<usize> {
match *self {
Function::Virtual(ref f) => f.borrow().rt_handles.clone(),
Function::Native(_) => Vec::new()
}
}
fn as_any(&self) -> &Any {
self as &Any
}
fn as_any_mut(&mut self) -> &mut Any {
self as &mut Any
}
fn call(&self, executor: &mut ExecutorImpl) -> Value {
match *self {
Function::Virtual(ref vf) => {
let vf = vf.borrow();
if let Some(this) = vf.this {
executor.get_current_frame().set_this(this);
}
executor.eval_basic_blocks(vf.basic_blocks.as_slice(), 0)
},
Function::Native(ref nf) => {
nf(executor)
}
}
}
}
impl Function {
pub fn from_basic_blocks(blocks: Vec<BasicBlock>) -> Function {
let vf = VirtualFunction {
basic_blocks: blocks,
rt_handles: Vec::new(),
should_optimize: false,
this: None
};
vf.validate().unwrap_or_else(|e| {
panic!(errors::VMError::from(e))
});
Function::Virtual(RefCell::new(vf))
}
pub fn bind_this(&self, this: Value) {
if let Function::Virtual(ref f) = *self {
if let Ok(mut f) = f.try_borrow_mut() {
if f.this.is_some() {
panic!(errors::VMError::from("Cannot rebind this"));
}
f.this = Some(this);
if let Value::Object(id) = this {
f.rt_handles.push(id);
}
} else {
panic!(errors::VMError::from("Cannot bind from inside the function"));
}
} else {
panic!(errors::VMError::from("Binding this is only supported on virtual functions"));
}
}
pub fn enable_optimization(&mut self) {
if let Function::Virtual(ref mut f) = *self {
f.borrow_mut().should_optimize = true;
}
}
pub fn from_native(nf: NativeFunction) -> Function {
Function::Native(nf)
}
pub fn to_virtual_info(&self) -> Option<VirtualFunctionInfo> {
match *self {
Function::Virtual(ref vf) => Some(VirtualFunctionInfo {
basic_blocks: vf.borrow().basic_blocks.clone()
}),
Function::Native(_) => None
}
}
pub fn from_virtual_info(vinfo: VirtualFunctionInfo) -> Self {
Function::from_basic_blocks(vinfo.basic_blocks)
}
pub fn static_optimize(&self, pool: &mut ObjectPool) {
if let Function::Virtual(ref f) = *self {
if let Ok(mut f) = f.try_borrow_mut() {
if f.should_optimize {
f.static_optimize(pool);
}
} else {
panic!(errors::VMError::from("Cannot optimize virtual functions within itself"));
}
}
}
pub fn dynamic_optimize(&self, pool: &mut ObjectPool) {
if let Function::Virtual(ref f) = *self {
if let Ok(mut f) = f.try_borrow_mut() {
if f.should_optimize {
f.dynamic_optimize(pool);
}
} else {
panic!(errors::VMError::from("Cannot optimize virtual functions within itself"));
}
}
}
}
impl VirtualFunction {
fn static_optimize(&mut self, pool: &mut ObjectPool) {
let mut optimizer = FunctionOptimizer::new(&mut self.basic_blocks, &mut self.rt_handles, pool);
optimizer.set_binded_this(self.this);
optimizer.static_optimize();
}
fn dynamic_optimize(&mut self, pool: &mut ObjectPool) {
let mut optimizer = FunctionOptimizer::new(&mut self.basic_blocks, &mut self.rt_handles, pool);
optimizer.set_binded_this(self.this);
optimizer.dynamic_optimize();
}
pub fn validate(&self) -> Result<(), errors::ValidateError> {
self.validate_basic_blocks()?;
self.validate_branch_targets()?;
Ok(())
}
pub fn validate_basic_blocks(&self) -> Result<(), errors::ValidateError> {
for bb in &self.basic_blocks {
bb.validate(false)?;
}
Ok(())
}
pub fn validate_branch_targets(&self) -> Result<(), errors::ValidateError> {
let blocks = &self.basic_blocks;
for bb in blocks {
let mut found_error: bool = false;
let (fst, snd) = bb.branch_targets();
if let Some(fst) = fst {
if fst >= blocks.len() {
found_error = true;
}
}
if let Some(snd) = snd {
if snd >= blocks.len() {
found_error = true;
}
}
if found_error {
return Err(errors::ValidateError::new("Invalid branch target(s)"));
}
}
Ok(())
}
}