use std::mem;
use std::any::Any;
use std::cell::UnsafeCell;
use stack::Stack;
use reg_context::Context as RegContext;
thread_local!(static CONTEXT_STACK: UnsafeCell<Box<ContextStack>>
= UnsafeCell::new(ContextStack::new()));
thread_local!(static ROOT_CONTEXT: UnsafeCell<Context>
= UnsafeCell::new(Context::new(0)));
#[allow(dead_code)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Error {
Cancel,
TypeErr,
StackErr,
ContextErr,
}
pub struct Context {
pub regs: RegContext,
pub stack: Stack,
pub para: *mut Any,
pub ret: *mut Any,
pub _ref: u32,
pub err: Option<Error>,
}
impl Context {
pub fn new(size: usize) -> Context {
Context {
regs: RegContext::empty(),
stack: Stack::new(size),
para: unsafe { mem::transmute(&0 as &Any) },
ret: unsafe { mem::transmute(&0 as &Any) },
_ref: 0,
err: None,
}
}
pub fn is_generator(&self) -> bool {
self.stack.size() > 0
}
#[inline]
pub fn get_para<A>(&self) -> Option<A>
where A: Any
{
let para = unsafe { &mut *self.para };
if para.is::<Option<A>>() {
para.downcast_mut::<Option<A>>().unwrap().take()
} else {
error!("get yield type error detected");
panic!(Error::TypeErr);
}
}
#[inline]
pub fn set_ret<T>(&mut self, v: T)
where T: Any
{
let ret = unsafe { &mut *self.ret };
if ret.is::<Option<T>>() {
let val = ret.downcast_mut::<Option<T>>();
mem::replace(val.unwrap(), Some(v));
} else {
error!("yield type error detected");
panic!(Error::TypeErr);
}
}
}
pub struct ContextStack {
stack: Vec<*mut Context>,
}
impl ContextStack {
fn new() -> Box<ContextStack> {
let mut r = Box::new(ContextStack { stack: Vec::new() });
r.push(ROOT_CONTEXT.with(|env| env.get()));
r
}
#[inline]
pub fn current() -> &'static mut ContextStack {
CONTEXT_STACK.with(|env| unsafe { &mut *env.get() })
}
#[inline]
pub fn push(&mut self, context: *mut Context) {
self.stack.push(context);
}
#[inline]
pub fn pop(&mut self) -> Option<*mut Context> {
self.stack.pop()
}
#[inline]
pub fn top(&self) -> &'static mut Context {
unsafe { &mut **self.stack.last().unwrap() }
}
}
#[cfg(test)]
mod test {
use super::ContextStack;
#[test]
fn test_is_context() {
let ctx = ContextStack::current().top();
assert!(!ctx.is_generator());
}
}