| use std::prelude::v1::*; |
| use std::cell::Cell; |
| use std::collections::HashSet; |
| use smallvec::SmallVec; |
| use errors; |
| use value::Value; |
| use opcode::StackMapPattern; |
| use object_pool::ObjectPool; |
| |
| fixed_array!(FixedArray32, 32); |
| |
| pub struct CallStack { |
| frames: Vec<Frame>, |
| n_frames: usize, |
| limit: Option<usize> |
| } |
| |
| pub type FrameHandle = Frame; |
| |
| // [unsafe] |
| // These fields are guaranteed to be accessed properly (as an implementation detail). |
| pub struct Frame { |
| this: Cell<Value>, |
| arguments: FixedArray32<Value>, |
| locals: FixedArray32<Value>, |
| pub(crate) exec_stack: FixedArray32<Value> |
| } |
| |
| impl CallStack { |
| pub fn new(len: usize) -> CallStack { |
| let mut frames = Vec::with_capacity(len); |
| for _ in 0..len { |
| frames.push(Frame::new()); |
| } |
| |
| CallStack { |
| frames: frames, |
| n_frames: 1, // one 'initial' frame |
| limit: None |
| } |
| } |
| |
| pub fn set_limit(&mut self, limit: usize) { |
| self.limit = Some(limit); |
| } |
| |
| pub fn push(&mut self) { |
| if self.n_frames >= self.frames.len() { |
| panic!(errors::VMError::from(errors::RuntimeError::new("Virtual stack overflow"))); |
| } |
| if let Some(limit) = self.limit { |
| if self.n_frames >= limit { |
| panic!(errors::VMError::from(errors::RuntimeError::new("Maximum stack depth exceeded"))); |
| } |
| } |
| self.n_frames += 1; |
| } |
| |
| pub fn pop(&mut self) { |
| if self.n_frames <= 0 { |
| panic!(errors::VMError::from(errors::RuntimeError::new("Virtual stack underflow"))); |
| } |
| self.frames[self.n_frames - 1].reset(); |
| self.n_frames -= 1; |
| } |
| |
| pub fn top(&self) -> &Frame { |
| if self.n_frames <= 0 { |
| panic!(errors::VMError::from(errors::RuntimeError::new("Virtual stack underflow"))); |
| } |
| &self.frames[self.n_frames - 1] |
| } |
| |
| pub fn collect_objects(&self) -> Vec<usize> { |
| let mut objs = HashSet::new(); |
| for i in 0..self.n_frames { |
| let frame = &self.frames[i]; |
| if let Value::Object(id) = frame.this.get() { |
| objs.insert(id); |
| } |
| for i in 0..frame.arguments.len() { |
| let v = frame.arguments.get(i).unwrap(); |
| if let Value::Object(id) = v { |
| objs.insert(id); |
| } |
| } |
| for i in 0..frame.locals.len() { |
| let v = frame.locals.get(i).unwrap(); |
| if let Value::Object(id) = v { |
| objs.insert(id); |
| } |
| } |
| for i in 0..frame.exec_stack.len() { |
| let v = frame.exec_stack.get(i).unwrap(); |
| if let Value::Object(id) = v { |
| objs.insert(id); |
| } |
| } |
| } |
| objs.into_iter().collect() |
| } |
| } |
| |
| impl Frame { |
| pub fn new() -> Frame { |
| Frame { |
| this: Cell::new(Value::Null), |
| arguments: FixedArray32::new(Value::Null), |
| locals: FixedArray32::new(Value::Null), |
| exec_stack: FixedArray32::new(Value::Null) |
| } |
| } |
| |
| fn reset(&self) { |
| self.this.set(Value::Null); |
| self.arguments.clear(); |
| self.locals.clear(); |
| self.exec_stack.clear(); |
| } |
| |
| pub fn init_with_arguments(&self, this: Value, args: &[Value]) { |
| self.this.set(this); |
| for arg in args { |
| self.arguments.push(*arg); |
| } |
| } |
| |
| #[inline] |
| pub fn push_exec(&self, obj: Value) { |
| self.exec_stack.push(obj); |
| } |
| |
| #[inline] |
| pub fn pop_exec(&self) -> Value { |
| self.exec_stack.pop() |
| } |
| |
| #[inline] |
| pub fn dup_exec(&self) { |
| self.exec_stack.push(self.exec_stack.top()); |
| } |
| |
| pub fn map_exec(&self, p: &StackMapPattern, pool: &mut ObjectPool) { |
| let mut new_values: SmallVec<[Value; 4]> = SmallVec::with_capacity(p.map.len()); |
| for loc in &p.map { |
| new_values.push(loc.extract(self, pool)); |
| } |
| |
| if p.end_state < 0 { |
| for _ in 0..(-p.end_state) { |
| self.exec_stack.pop(); |
| } |
| } else { |
| for _ in 0..p.end_state { |
| self.exec_stack.push(Value::Null); |
| } |
| } |
| |
| for i in 0..new_values.len() { |
| let sv_id = self.exec_stack.len() - 1 - i; |
| let nv_id = new_values.len() - 1 - i; |
| self.exec_stack.set(sv_id, new_values[nv_id]); |
| } |
| } |
| |
| pub fn bulk_load(&self, values: &[Value]) { |
| for v in values { |
| self.exec_stack.push(*v); |
| } |
| } |
| |
| pub fn reset_locals(&self, n_slots: usize) { |
| self.locals.clear(); |
| for _ in 0..n_slots { |
| self.locals.push(Value::Null); |
| } |
| } |
| |
| #[inline] |
| pub fn get_local(&self, ind: usize) -> Value { |
| self.locals.get(ind).unwrap() |
| } |
| |
| #[inline] |
| pub fn set_local(&self, ind: usize, obj: Value) { |
| self.locals.set(ind, obj); |
| } |
| |
| #[inline] |
| pub fn get_argument(&self, id: usize) -> Option<Value> { |
| self.arguments.get(id) |
| } |
| |
| #[inline] |
| pub fn must_get_argument(&self, id: usize) -> Value { |
| self.get_argument(id).unwrap_or_else(|| { |
| panic!(errors::VMError::from(errors::RuntimeError::new("Argument index out of bound"))) |
| }) |
| } |
| |
| #[inline] |
| pub fn get_n_arguments(&self) -> usize { |
| self.arguments.len() |
| } |
| |
| #[inline] |
| pub fn get_this(&self) -> Value { |
| self.this.get() |
| } |
| |
| #[inline] |
| pub fn set_this(&self, this: Value) { |
| self.this.set(this); |
| } |
| } |