<commit_before>use std::cell::RefCell;
use std::collections::hash_map::{Entry, HashMap};
use std::collections::hash_set::HashSet;
use std::mem;
use std::rc::Rc;

use alloc::AllocBox;
use gc_error::{GcError, Result};
use js_types::js_var::{JsPtrEnum, JsType, JsVar};
use js_types::binding::Binding;
use js_types::allocator::Allocator;

// Tunable GC parameter. Probably should not be a constant, but good enough for now.
const GC_THRESHOLD: usize = 64;

/// A logical scope in the AST. Represents any scoped block of Javascript code.
/// roots: A set of all root references into the heap
/// parent: An optional parent scope, e.g. the caller of this function scope,
///         or the function that owns an `if` statement
/// heap: A shared reference to the heap allocator.
/// stack: The stack of the current scope, containing all variables allocated
///        by this scope.
pub struct Scope {
    roots: HashSet<Binding>,
    pub parent: Option<Box<Scope>>,
    heap: Rc<RefCell<AllocBox>>,
    stack: HashMap<Binding, JsVar>,
}

impl Scope {
    /// Create a new, parentless scope node.
    pub fn new(heap: &Rc<RefCell<AllocBox>>) -> Scope {
        Scope {
            roots: HashSet::new(),
            parent: None,
            heap: heap.clone(),
            stack: HashMap::new(),
        }
    }

    /// Create a scope as a child of another scope. Clones all of bindings of
    /// root references from the parent.
    pub fn as_child(parent: Scope, heap: &Rc<RefCell<AllocBox>>) -> Scope {
        Scope {
            roots: parent.roots.clone(),
            parent: Some(box parent),
            heap: heap.clone(),
            stack: HashMap::new(),
        }
    }

    /// Sets the parent of a scope, and clones and unions its root bindings.
    pub fn set_parent(&mut self, parent: Scope) {
        self.roots = self.roots.union(&parent.roots).cloned().collect();
        self.parent = Some(box parent);
    }

    /// Push a new JsVar onto the stack, and maybe allocate a pointer in the heap.
    pub fn push(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<()> {
        let res = match var.t {
            JsType::JsPtr =>
                if let Some(ptr) = ptr {
                    // Creating a new pointer creates a new root
                    self.roots.insert(var.binding.clone());
                    self.heap.borrow_mut().alloc(var.binding.clone(), ptr)
                } else {
                    return Err(GcError::PtrError);
                },
            _ => if let Some(_) = ptr { Err(GcError::PtrError) } else { Ok(()) },
        };
        self.stack.insert(var.binding.clone(), var);
        res
    }

    /// Take ownership of a variable (usually from another scope).
    pub fn own(&mut self, var: JsVar) {
        self.stack.insert(var.binding.clone(), var);
    }

    /// Return an optional copy of a variable and an optional pointer into the heap.
    pub fn get_var_copy(&self, bnd: &Binding) -> (Option<JsVar>, Option<JsPtrEnum>) {
        if let Some(var) = self.stack.get(bnd) {
            match var.t {
                JsType::JsPtr => {
                    if let Some(alloc) = self.heap.borrow().find_id(bnd) {
                        (Some(var.clone()), Some(alloc.borrow().clone()))
                    } else {
                        // This case should be impossible unless you have an
                        // invalid ptr, which should also be impossible.
                        (None, None)
                    }
                },
                _ => (Some(var.clone()), None),
            }
        } else if let Some(ref parent) = self.parent {
            // FIXME? This is slow.
            parent.get_var_copy(bnd)
        } else { (None, None) }
    }

    /// Try to update a variable that's been allocated.
    pub fn update_var(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<()> {
        match var.t {
            JsType::JsPtr =>
                if let Some(ptr) = ptr {
                    // A new root was potentially created
                    self.roots.insert(var.binding.clone());
                    self.heap.borrow_mut().update_ptr(&var.binding, ptr)
                } else {
                    Err(GcError::PtrError)
                },
            _ => {
                if let Some(_) = ptr { return Err(GcError::PtrError); }
                if let Entry::Occupied(mut view) = self.stack.entry(var.binding.clone()) {
                    // A root was potentially removed
                    self.roots.remove(&var.binding);
                    *view.get_mut() = var;
                    return Ok(());
                }
                // Hack to skirt mutable borrow of self lasting longer than it should
                if let Entry::Vacant(_) = self.stack.entry(var.binding.clone()) {
                    // TODO Need to call `push` on the root scope?
                    // Push into this scope and just give to parent when done?
                    return self.push(var, ptr);
                }
                unreachable!();
            },
        }
    }

    /// Called when a scope exits. Transfers the stack of this scope to its parent,
    /// and returns the parent scope, which may be `None`.
    pub fn transfer_stack(&mut self, gc_yield: bool) -> Option<Box<Scope>> {
        if gc_yield {
            // The interpreter says we can GC now
            self.heap.borrow_mut().mark_roots(&self.roots);
            self.heap.borrow_mut().mark_ptrs();
            self.heap.borrow_mut().sweep_ptrs();
            // Pop all of the roots we just deleted
            for bnd in &self.roots {
                if let None = self.heap.borrow().find_id(bnd) {
                    self.stack.remove(bnd);
                }
            }
        }
        if let Some(ref mut parent) = self.parent {
            for (_, var) in self.stack.drain() {
                if let JsType::JsPtr = var.t {
                        // Mangle each binding before giving it to the parent
                        // scope. This avoids binding collisions, and helps
                        // identify to a human observer which bindings are
                        // not from the current scope.
                        let mut mangled_var = var.clone();
                        mangled_var.binding = Binding::mangle(var.binding);
                        parent.own(mangled_var);
                }
            }
            parent.roots = parent.roots.union(&self.roots).cloned().collect();
        }
        mem::replace(&mut self.parent, None)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use gc_error::GcError;
    use js_types::js_var::{JsPtrEnum, JsKey, JsKeyEnum, JsType};
    use js_types::binding::Binding;
    use js_types::js_str::JsStrStruct;
    use test_utils;

    #[test]
    fn test_new_scope() {
        let heap = test_utils::make_alloc_box();
        let test_scope = Scope::new(&heap);
        assert!(test_scope.parent.is_none());
    }

    #[test]
    fn test_as_child_scope() {
        let heap = test_utils::make_alloc_box();
        let parent_scope = Scope::new(&heap);
        let test_scope = Scope::as_child(parent_scope, &heap);
        assert!(test_scope.parent.is_some());
    }

    #[test]
    fn test_set_parent() {
        let heap = test_utils::make_alloc_box();
        let parent_scope = Scope::new(&heap);
        let mut test_scope = Scope::new(&heap);
        assert!(test_scope.parent.is_none());
        test_scope.set_parent(parent_scope);
        assert!(test_scope.parent.is_some());
    }

    #[test]
    fn test_push() {
        let heap = test_utils::make_alloc_box();
        let mut test_scope = Scope::new(&heap);
        let (var, ptr, _) = test_utils::make_str("test");
        assert!(test_scope.push(var, Some(ptr)).is_ok());
        assert_eq!(test_scope.heap.borrow().len(), 1);
        let var = test_utils::make_num(1.);
        assert!(test_scope.push(var, None).is_ok());
        assert_eq!(test_scope.heap.borrow().len(), 1);
    }

    #[test]
    fn test_push_fail() {
        let heap = test_utils::make_alloc_box();
        let mut test_scope = Scope::new(&heap);
        let (var, ptr, _) = test_utils::make_str("test");
        let res = test_scope.push(var, None);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::PtrError)));
        assert!(test_scope.heap.borrow().is_empty());
        let var = test_utils::make_num(1.);
        let res = test_scope.push(var, Some(ptr));
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::PtrError)));
        assert!(test_scope.heap.borrow().is_empty());
    }

    #[test]
    fn test_get_var_copy() {
        let heap = test_utils::make_alloc_box();
        let mut test_scope = Scope::new(&heap);
        let (x, x_ptr, x_bnd) = test_utils::make_str("x");
        test_scope.push(x, Some(x_ptr)).unwrap();

        let (var_copy, ptr_copy) = test_scope.get_var_copy(&x_bnd);
        assert!(var_copy.is_some());
        assert!(ptr_copy.is_some());
    }

    #[test]
    fn test_get_var_copy_fail() {
        let heap = test_utils::make_alloc_box();
        let mut test_scope = Scope::new(&heap);
        let (bad_copy, ptr_copy) = test_scope.get_var_copy(&Binding::anon());
        assert!(bad_copy.is_none());
        assert!(ptr_copy.is_none());
    }

    #[test]
    fn test_get_var_copy_from_parent_scope() {
        let heap = test_utils::make_alloc_box();
        let mut parent_scope = Scope::new(&heap);
        let (x, x_ptr, x_bnd) = test_utils::make_str("x");
        parent_scope.push(x, Some(x_ptr)).unwrap();

        let child_scope = Scope::as_child(parent_scope, &heap);

        let (var_copy, ptr_copy) = child_scope.get_var_copy(&x_bnd);
        assert!(var_copy.is_some());
        assert!(ptr_copy.is_some());
    }

    #[test]
    fn test_update_var() {
        let heap = test_utils::make_alloc_box();
        let mut test_scope = Scope::new(&heap);
        let (x, x_ptr, x_bnd) = test_utils::make_str("x");
        assert!(test_scope.push(x, Some(x_ptr)).is_ok());
        let (update, _) = test_scope.get_var_copy(&x_bnd);
        let update_ptr = Some(JsPtrEnum::JsStr(JsStrStruct::new("test")));
        assert!(test_scope.update_var(update.unwrap(), update_ptr).is_ok());

        let (update, update_ptr) = test_scope.get_var_copy(&x_bnd);
        match update_ptr.unwrap() {
            JsPtrEnum::JsStr(JsStrStruct{text: ref s}) => assert_eq!(s, "test"),
            _ => unreachable!(),
        }
        assert_eq!(update.unwrap().binding, x_bnd);
    }

    #[test]
    fn test_update_var_fail() {
        let heap = test_utils::make_alloc_box();
        let mut test_scope = Scope::new(&heap);
        let (x, x_ptr, x_bnd) = test_utils::make_str("x");
        assert!(test_scope.push(x, Some(x_ptr)).is_ok());
        let (mut update, update_ptr) = test_scope.get_var_copy(&x_bnd);
        let res = test_scope.update_var(update.clone().unwrap(), None);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::PtrError)));

        let mut update = update.unwrap();
        update.t = JsType::JsNum(1.);
        let res = test_scope.update_var(update, update_ptr);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::PtrError)));
    }

    #[test]
    fn test_transfer_stack() {
        let heap = test_utils::make_alloc_box();
        let mut parent_scope = Scope::new(&heap);
        {
            let mut test_scope = Scope::as_child(parent_scope, &heap);
            test_scope.push(test_utils::make_num(0.), None).unwrap();
            test_scope.push(test_utils::make_num(1.), None).unwrap();
            test_scope.push(test_utils::make_num(2.), None).unwrap();
            let kvs = vec![(JsKey::new(JsKeyEnum::JsBool(true)),
                            test_utils::make_num(1.), None)];
            let (var, ptr, _) = test_utils::make_obj(kvs, heap.clone());
            test_scope.push(var, Some(ptr)).unwrap();
            parent_scope = *test_scope.transfer_stack(false).unwrap();
        }
        assert_eq!(parent_scope.stack.len(), 1);
    }

    #[test]
    fn test_transfer_stack_with_yield() {
        let heap = test_utils::make_alloc_box();
        // Make a scope
        let mut parent_scope = Scope::new(&heap);
        {
            // Push a child scope
            let mut test_scope = Scope::as_child(parent_scope, &heap);
            // Allocate some non-root variables (numbers)
            test_scope.push(test_utils::make_num(0.), None).unwrap();
            test_scope.push(test_utils::make_num(1.), None).unwrap();
            test_scope.push(test_utils::make_num(2.), None).unwrap();

            // Make a string to put into an object
            // (so it's heap-allocated and we can lose its ref from the object)
            let (var, ptr, _) = test_utils::make_str("test");

            // Create an obj of { true: 1.0, false: heap("test") }
            let kvs = vec![(JsKey::new(JsKeyEnum::JsBool(true)),
                            test_utils::make_num(1.), None),
                           (JsKey::new(JsKeyEnum::JsBool(false)),
                            var, Some(ptr))];
            let key_bnd = kvs[1].0.binding.clone();
            let (var, ptr, bnd) = test_utils::make_obj(kvs, heap.clone());

            // Push the obj into the current scope
            test_scope.push(var, Some(ptr)).unwrap();

            // Replace the string in the object with something else so it's no longer live
            let copy = test_scope.get_var_copy(&bnd);
            let (var_cp, mut ptr_cp) = (copy.0.unwrap(), copy.1.unwrap());
            let key = JsKey { binding: key_bnd, k: JsKeyEnum::JsBool(false) };
            match ptr_cp {
                JsPtrEnum::JsObj(ref mut obj) => { obj.dict.insert(key, test_utils::make_num(-1.)); }
                _ => unreachable!()
            }
            test_scope.update_var(var_cp, Some(ptr_cp)).unwrap();

            // Kill the current scope & give its refs to the parent,
            // allowing the GC to kick in beforehand.
            parent_scope = *test_scope.transfer_stack(true).unwrap();
        }
        // The object we created above should still exist
        assert_eq!(parent_scope.stack.len(), 1);
        assert_eq!(heap.borrow().len(), 1);
    }

}
<commit_msg>Moved Scope::as_child into the tests submodule since it was only used there<commit_after>use std::cell::RefCell;
use std::collections::hash_map::{Entry, HashMap};
use std::collections::hash_set::HashSet;
use std::mem;
use std::rc::Rc;

use alloc::AllocBox;
use gc_error::{GcError, Result};
use js_types::js_var::{JsPtrEnum, JsType, JsVar};
use js_types::binding::Binding;
use js_types::allocator::Allocator;

// Tunable GC parameter. Probably should not be a constant, but good enough for now.
const GC_THRESHOLD: usize = 64;

/// A logical scope in the AST. Represents any scoped block of Javascript code.
/// roots: A set of all root references into the heap
/// parent: An optional parent scope, e.g. the caller of this function scope,
///         or the function that owns an `if` statement
/// heap: A shared reference to the heap allocator.
/// stack: The stack of the current scope, containing all variables allocated
///        by this scope.
pub struct Scope {
    roots: HashSet<Binding>,
    pub parent: Option<Box<Scope>>,
    heap: Rc<RefCell<AllocBox>>,
    stack: HashMap<Binding, JsVar>,
}

impl Scope {
    /// Create a new, parentless scope node.
    pub fn new(heap: &Rc<RefCell<AllocBox>>) -> Scope {
        Scope {
            roots: HashSet::new(),
            parent: None,
            heap: heap.clone(),
            stack: HashMap::new(),
        }
    }

    /// Sets the parent of a scope, and clones and unions its root bindings.
    pub fn set_parent(&mut self, parent: Scope) {
        self.roots = self.roots.union(&parent.roots).cloned().collect();
        self.parent = Some(box parent);
    }

    /// Push a new JsVar onto the stack, and maybe allocate a pointer in the heap.
    pub fn push(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<()> {
        let res = match var.t {
            JsType::JsPtr =>
                if let Some(ptr) = ptr {
                    // Creating a new pointer creates a new root
                    self.roots.insert(var.binding.clone());
                    self.heap.borrow_mut().alloc(var.binding.clone(), ptr)
                } else {
                    return Err(GcError::PtrError);
                },
            _ => if let Some(_) = ptr { Err(GcError::PtrError) } else { Ok(()) },
        };
        self.stack.insert(var.binding.clone(), var);
        res
    }

    /// Take ownership of a variable (usually from another scope).
    pub fn own(&mut self, var: JsVar) {
        self.stack.insert(var.binding.clone(), var);
    }

    /// Return an optional copy of a variable and an optional pointer into the heap.
    pub fn get_var_copy(&self, bnd: &Binding) -> (Option<JsVar>, Option<JsPtrEnum>) {
        if let Some(var) = self.stack.get(bnd) {
            match var.t {
                JsType::JsPtr => {
                    if let Some(alloc) = self.heap.borrow().find_id(bnd) {
                        (Some(var.clone()), Some(alloc.borrow().clone()))
                    } else {
                        // This case should be impossible unless you have an
                        // invalid ptr, which should also be impossible.
                        (None, None)
                    }
                },
                _ => (Some(var.clone()), None),
            }
        } else if let Some(ref parent) = self.parent {
            // FIXME? This is slow.
            parent.get_var_copy(bnd)
        } else { (None, None) }
    }

    /// Try to update a variable that's been allocated.
    pub fn update_var(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<()> {
        match var.t {
            JsType::JsPtr =>
                if let Some(ptr) = ptr {
                    // A new root was potentially created
                    self.roots.insert(var.binding.clone());
                    self.heap.borrow_mut().update_ptr(&var.binding, ptr)
                } else {
                    Err(GcError::PtrError)
                },
            _ => {
                if let Some(_) = ptr { return Err(GcError::PtrError); }
                if let Entry::Occupied(mut view) = self.stack.entry(var.binding.clone()) {
                    // A root was potentially removed
                    self.roots.remove(&var.binding);
                    *view.get_mut() = var;
                    return Ok(());
                }
                // Hack to skirt mutable borrow of self lasting longer than it should
                if let Entry::Vacant(_) = self.stack.entry(var.binding.clone()) {
                    // TODO Need to call `push` on the root scope?
                    // Push into this scope and just give to parent when done?
                    return self.push(var, ptr);
                }
                unreachable!();
            },
        }
    }

    /// Called when a scope exits. Transfers the stack of this scope to its parent,
    /// and returns the parent scope, which may be `None`.
    pub fn transfer_stack(&mut self, gc_yield: bool) -> Option<Box<Scope>> {
        if gc_yield {
            // The interpreter says we can GC now
            self.heap.borrow_mut().mark_roots(&self.roots);
            self.heap.borrow_mut().mark_ptrs();
            self.heap.borrow_mut().sweep_ptrs();
            // Pop all of the roots we just deleted
            for bnd in &self.roots {
                if let None = self.heap.borrow().find_id(bnd) {
                    self.stack.remove(bnd);
                }
            }
        }
        if let Some(ref mut parent) = self.parent {
            for (_, var) in self.stack.drain() {
                if let JsType::JsPtr = var.t {
                        // Mangle each binding before giving it to the parent
                        // scope. This avoids binding collisions, and helps
                        // identify to a human observer which bindings are
                        // not from the current scope.
                        let mut mangled_var = var.clone();
                        mangled_var.binding = Binding::mangle(var.binding);
                        parent.own(mangled_var);
                }
            }
            parent.roots = parent.roots.union(&self.roots).cloned().collect();
        }
        mem::replace(&mut self.parent, None)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::cell::RefCell;
    use std::collections::hash_map::HashMap;
    use std::rc::Rc;

    use alloc::AllocBox;
    use gc_error::GcError;
    use js_types::js_var::{JsPtrEnum, JsKey, JsKeyEnum, JsType};
    use js_types::binding::Binding;
    use js_types::js_str::JsStrStruct;
    use test_utils;

    fn new_scope_as_child(parent: Scope, heap: &Rc<RefCell<AllocBox>>) -> Scope {
        Scope {
            roots: parent.roots.clone(),
            parent: Some(box parent),
            heap: heap.clone(),
            stack: HashMap::new(),
        }
    }

    #[test]
    fn test_new_scope() {
        let heap = test_utils::make_alloc_box();
        let test_scope = Scope::new(&heap);
        assert!(test_scope.parent.is_none());
    }

    #[test]
    fn test_as_child_scope() {
        let heap = test_utils::make_alloc_box();
        let parent_scope = Scope::new(&heap);
        let test_scope = new_scope_as_child(parent_scope, &heap);
        assert!(test_scope.parent.is_some());
    }

    #[test]
    fn test_set_parent() {
        let heap = test_utils::make_alloc_box();
        let parent_scope = Scope::new(&heap);
        let mut test_scope = Scope::new(&heap);
        assert!(test_scope.parent.is_none());
        test_scope.set_parent(parent_scope);
        assert!(test_scope.parent.is_some());
    }

    #[test]
    fn test_push() {
        let heap = test_utils::make_alloc_box();
        let mut test_scope = Scope::new(&heap);
        let (var, ptr, _) = test_utils::make_str("test");
        assert!(test_scope.push(var, Some(ptr)).is_ok());
        assert_eq!(test_scope.heap.borrow().len(), 1);
        let var = test_utils::make_num(1.);
        assert!(test_scope.push(var, None).is_ok());
        assert_eq!(test_scope.heap.borrow().len(), 1);
    }

    #[test]
    fn test_push_fail() {
        let heap = test_utils::make_alloc_box();
        let mut test_scope = Scope::new(&heap);
        let (var, ptr, _) = test_utils::make_str("test");
        let res = test_scope.push(var, None);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::PtrError)));
        assert!(test_scope.heap.borrow().is_empty());
        let var = test_utils::make_num(1.);
        let res = test_scope.push(var, Some(ptr));
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::PtrError)));
        assert!(test_scope.heap.borrow().is_empty());
    }

    #[test]
    fn test_get_var_copy() {
        let heap = test_utils::make_alloc_box();
        let mut test_scope = Scope::new(&heap);
        let (x, x_ptr, x_bnd) = test_utils::make_str("x");
        test_scope.push(x, Some(x_ptr)).unwrap();

        let (var_copy, ptr_copy) = test_scope.get_var_copy(&x_bnd);
        assert!(var_copy.is_some());
        assert!(ptr_copy.is_some());
    }

    #[test]
    fn test_get_var_copy_fail() {
        let heap = test_utils::make_alloc_box();
        let mut test_scope = Scope::new(&heap);
        let (bad_copy, ptr_copy) = test_scope.get_var_copy(&Binding::anon());
        assert!(bad_copy.is_none());
        assert!(ptr_copy.is_none());
    }

    #[test]
    fn test_get_var_copy_from_parent_scope() {
        let heap = test_utils::make_alloc_box();
        let mut parent_scope = Scope::new(&heap);
        let (x, x_ptr, x_bnd) = test_utils::make_str("x");
        parent_scope.push(x, Some(x_ptr)).unwrap();

        let child_scope = new_scope_as_child(parent_scope, &heap);

        let (var_copy, ptr_copy) = child_scope.get_var_copy(&x_bnd);
        assert!(var_copy.is_some());
        assert!(ptr_copy.is_some());
    }

    #[test]
    fn test_update_var() {
        let heap = test_utils::make_alloc_box();
        let mut test_scope = Scope::new(&heap);
        let (x, x_ptr, x_bnd) = test_utils::make_str("x");
        assert!(test_scope.push(x, Some(x_ptr)).is_ok());
        let (update, _) = test_scope.get_var_copy(&x_bnd);
        let update_ptr = Some(JsPtrEnum::JsStr(JsStrStruct::new("test")));
        assert!(test_scope.update_var(update.unwrap(), update_ptr).is_ok());

        let (update, update_ptr) = test_scope.get_var_copy(&x_bnd);
        match update_ptr.unwrap() {
            JsPtrEnum::JsStr(JsStrStruct{text: ref s}) => assert_eq!(s, "test"),
            _ => unreachable!(),
        }
        assert_eq!(update.unwrap().binding, x_bnd);
    }

    #[test]
    fn test_update_var_fail() {
        let heap = test_utils::make_alloc_box();
        let mut test_scope = Scope::new(&heap);
        let (x, x_ptr, x_bnd) = test_utils::make_str("x");
        assert!(test_scope.push(x, Some(x_ptr)).is_ok());
        let (mut update, update_ptr) = test_scope.get_var_copy(&x_bnd);
        let res = test_scope.update_var(update.clone().unwrap(), None);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::PtrError)));

        let mut update = update.unwrap();
        update.t = JsType::JsNum(1.);
        let res = test_scope.update_var(update, update_ptr);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::PtrError)));
    }

    #[test]
    fn test_transfer_stack() {
        let heap = test_utils::make_alloc_box();
        let mut parent_scope = Scope::new(&heap);
        {
            let mut test_scope = new_scope_as_child(parent_scope, &heap);
            test_scope.push(test_utils::make_num(0.), None).unwrap();
            test_scope.push(test_utils::make_num(1.), None).unwrap();
            test_scope.push(test_utils::make_num(2.), None).unwrap();
            let kvs = vec![(JsKey::new(JsKeyEnum::JsBool(true)),
                            test_utils::make_num(1.), None)];
            let (var, ptr, _) = test_utils::make_obj(kvs, heap.clone());
            test_scope.push(var, Some(ptr)).unwrap();
            parent_scope = *test_scope.transfer_stack(false).unwrap();
        }
        assert_eq!(parent_scope.stack.len(), 1);
    }

    #[test]
    fn test_transfer_stack_with_yield() {
        let heap = test_utils::make_alloc_box();
        // Make a scope
        let mut parent_scope = Scope::new(&heap);
        {
            // Push a child scope
            let mut test_scope = new_scope_as_child(parent_scope, &heap);
            // Allocate some non-root variables (numbers)
            test_scope.push(test_utils::make_num(0.), None).unwrap();
            test_scope.push(test_utils::make_num(1.), None).unwrap();
            test_scope.push(test_utils::make_num(2.), None).unwrap();

            // Make a string to put into an object
            // (so it's heap-allocated and we can lose its ref from the object)
            let (var, ptr, _) = test_utils::make_str("test");

            // Create an obj of { true: 1.0, false: heap("test") }
            let kvs = vec![(JsKey::new(JsKeyEnum::JsBool(true)),
                            test_utils::make_num(1.), None),
                           (JsKey::new(JsKeyEnum::JsBool(false)),
                            var, Some(ptr))];
            let key_bnd = kvs[1].0.binding.clone();
            let (var, ptr, bnd) = test_utils::make_obj(kvs, heap.clone());

            // Push the obj into the current scope
            test_scope.push(var, Some(ptr)).unwrap();

            // Replace the string in the object with something else so it's no longer live
            let copy = test_scope.get_var_copy(&bnd);
            let (var_cp, mut ptr_cp) = (copy.0.unwrap(), copy.1.unwrap());
            let key = JsKey { binding: key_bnd, k: JsKeyEnum::JsBool(false) };
            match ptr_cp {
                JsPtrEnum::JsObj(ref mut obj) => { obj.dict.insert(key, test_utils::make_num(-1.)); }
                _ => unreachable!()
            }
            test_scope.update_var(var_cp, Some(ptr_cp)).unwrap();

            // Kill the current scope & give its refs to the parent,
            // allowing the GC to kick in beforehand.
            parent_scope = *test_scope.transfer_stack(true).unwrap();
        }
        // The object we created above should still exist
        assert_eq!(parent_scope.stack.len(), 1);
        assert_eq!(heap.borrow().len(), 1);
    }

}
<|endoftext|>


<commit_before>//! The main scheduler logic.
//!
//! The scheduler is implemented as a singleton in order to make it easy for code anywhere in the
//! project to make use of async functionality. The actual scheduler instance is not publicly
//! accessible, instead we use various standalone functions like `start()` and `wait_for()` to
//! safely manage access to the scheduler.
//!
//! # Scheduling Work
//!
//! Use `scheduler::start()` to run some work asynchronously, getting an `Async<T>` representing the
//! result of the work. Use `Async::await()` to suspend the current fiber until the work completes
//! and get the result. By default, dropping an `Async<T>` will suspend the current fiber until
//! the work finishes, but you can use `Async::forget()` to ignore the result without blocking.
//!
//! # Sharing Data Between Work
//!
//! Unlike with `std::thread::spawn()`, it's possible for work started with `scheduler::start()`
//! to borrow data from the caller:
//!
//! ```
//!
//! ```

use fiber::{self, Fiber, FiberId};
use cell_extras::AtomicInitCell;
use std::boxed::FnBox;
use std::collections::{HashMap, HashSet, VecDeque};
use std::fmt::{self, Debug, Formatter};
use std::marker::PhantomData;
use std::mem;
use std::sync::{Condvar, Mutex, Once, ONCE_INIT};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::{self, Receiver};

const DEFAULT_STACK_SIZE: usize = 64 * 1024;

static CONDVAR: AtomicInitCell<Condvar> = AtomicInitCell::new();
static INSTANCE: AtomicInitCell<Mutex<Scheduler>> = AtomicInitCell::new();
static INSTANCE_INIT: Once = ONCE_INIT;
static WORK_COUNTER: AtomicUsize = AtomicUsize::new(1);

/// Represents the result of a computation that may finish at some point in the future.
///
/// Use `scheduler::start()` to run some work asynchronously, getting an `Async<T>` representing the
/// result of the work. Use `Async::await()` to suspend the current fiber until the work completes
/// and get the result. By default, dropping an `Async<T>` will suspend the current fiber until
/// the work finishes, but you can use `Async::forget()` to ignore the result without blocking.
///
/// # Sharing Data Across Work
///
/// It's possible to share
#[derive(Debug)]
pub struct Async<'a, T> {
    work: WorkId,
    receiver: Receiver<T>,
    _phantom: PhantomData<&'a FnMut()>,
}

impl<'a, T> Async<'a, T> {
    /// Suspend the current fiber until the async operation finishes.
    pub fn await(self) -> T {
        let result = {
            let Async { work, ref receiver, .. } = self;
            work.await();
            receiver.try_recv().expect("Failed to receive result of async computation")
        };

        // Don't run the destuctor since we've already waited on the result.
        mem::forget(self);

        result
    }
}

impl<T> Async<'static, T> {
    pub fn work_id(&self) -> WorkId {
        self.work
    }

    pub fn forget(self) {
        mem::forget(self);
    }
}

impl<'a, T> Drop for Async<'a, T> {
    fn drop(&mut self) {
        self.work.await();
    }
}

/// A shareable reference to a work unit, counterpart to `Async<T>`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WorkId(usize);

impl WorkId {
    /// Suspends the current fiber until this work unit has completed.
    ///
    /// If the work unit has already finished then `await()` will return immediately.
    pub fn await(self) {
        if Scheduler::with(|scheduler| scheduler.add_dependency(self)) {
            suspend();
        }
    }
}

/// Initializes a newly-spawned worker thread.
///
/// Prepares the worker thread by initializing it for Fiber usage.
// TODO: This should probably only be public within the crate. Only the engine should be using this,
// and only at startup, we probably don't want user code to be spawning threads anyway.
pub fn init_thread() {
    // Make sure the scheduler is initialized before first use.
    Scheduler::with(|_| {});

    // Manually convert current thread into a fiber because reasons.
    fiber::init();
}

// TODO: This should probably only be public within the crate. Only the engine should be using this,
// and only at startup, we probably don't want user code to be spawning threads anyway.
pub fn run_wait_fiber() {
    // Make sure the scheduler is initialized before first use.
    Scheduler::with(|_| {});

    // Setup this thread for running fibers and create an initial fiber for it. This will become
    // the wait fiber for this thread.
    fiber::init();

    fiber_routine();
}

pub fn start<'a, F, T>(func: F) -> Async<'a, T>
    where
    F: FnOnce() -> T,
    F: 'a + Send,
    T: 'a + Send,
{
    // Normally we can't box a closure with a non 'static lifetime because it could outlive its
    // borrowed data. In this case the lifetime parameter on the returned `Async` ensures that
    // the closure can't outlive the borrowed data, so we use this evil magic to convince the
    // compiler to allow us to box the closure.
    unsafe fn erase_lifetime<'a, F>(func: F) -> Box<FnBox()>
        where
        F: FnOnce(),
        F: 'a + Send,
    {
        let boxed_proc = Box::new(func);
        let proc_ptr = Box::into_raw(boxed_proc) as *mut FnBox();
        Box::from_raw(::std::mem::transmute(proc_ptr))
    }

    // Create the channel that'll be used to send the result of the operation to the `Async` object.
    let (sender, receiver) = mpsc::sync_channel(1);

    let work_id = WorkId(WORK_COUNTER.fetch_add(1, Ordering::Relaxed));

    let work_proc = unsafe {
        erase_lifetime(move || {
            let result = func();
            sender.try_send(result).expect("Failed to send async result");
        })
    };

    Scheduler::with(move |scheduler| scheduler.schedule_work(Work {
        func: work_proc,
        id: work_id,
    }));

    Async {
        work: work_id,
        receiver: receiver,
        _phantom: PhantomData,
    }
}

/// Suspends the current fiber and makes the wait fiber active.
///
/// Generally you shouldn't need to call this directly, but if you have one piece of code that
/// runs synchronously for a long time you can use `suspend()` to yield time to other work.
pub fn suspend() {
    let next_fiber = Scheduler::with(|scheduler| scheduler.next_fiber());

    let suspended = unsafe { next_fiber.resume() };

    Scheduler::with(move |scheduler| scheduler.handle_suspended(suspended));
}

fn fiber_routine() -> ! {
    loop {
        match Scheduler::with(|scheduler| scheduler.next()) {
            Some(NextWork::Work(Work { func, id })) => {
                Scheduler::with(|scheduler| scheduler.start_work(id));
                func();
                Scheduler::with(|scheduler| scheduler.finish_work(id));
            },
            Some(NextWork::Fiber(fiber)) => {
                let suspended = unsafe { fiber.resume() };
                Scheduler::with(move |scheduler| scheduler.handle_suspended(suspended));
            },
            None => {
                // If there's no new work and no fibers ready to run then we want to block the
                // thread until some becomes available.
                let mutex = INSTANCE.borrow();
                let condvar = CONDVAR.borrow();

                let _ = condvar
                    .wait(mutex.lock().expect("Scheduler mutex was poisoned"))
                    .expect("Scheduler mutex was poisoned");
            },
        }
    }
}

struct Work {
    func: Box<FnBox()>,
    id: WorkId,
}

impl Debug for Work {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> {
        write!(formatter, "Work {{ id: {:?} }}", self.id)
    }
}

enum NextWork {
    Work(Work),
    Fiber(Fiber),
}

struct Scheduler {
    /// Work units that are currently pending or in progress.
    current_work: HashSet<WorkId>,

    work_map: HashMap<FiberId, WorkId>,

    /// New units of work that haven't been started on a fiber yet.
    ///
    /// These are ready to be made active at any time.
    new_work: VecDeque<Work>,

    /// Fibers that have no pending dependencies.
    ///
    /// These are ready to be made active at any time.
    ready_fibers: VecDeque<Fiber>,

    /// A map specifying which pending fibers depend on which others.
    ///
    /// Once all of a fiber's dependencies complete it should be moved to `new_work`.
    dependencies: HashMap<FiberId, (Option<Fiber>, HashSet<WorkId>)>,

    // TODO: Should we distinguise between "finished" and "ready" fibers? My intuition is that we'd
    // want to give fibers that actively have work CPU time before we resume fibers that would be
    // pulling new work, but maybe not? If we threw them all into one queue I guess the worst case
    // scenario would be there's no work left, a bunch of empty fibers, and only a few fibers with
    // active work. In which case we might have to cycle through a bunch of fibers before we can
    // start doing actual work.
    finished: VecDeque<Fiber>,
}

unsafe impl Send for Scheduler {}

impl Scheduler {
    /// Provides safe access to the scheduler instance.
    ///
    /// # Fiber Switches
    ///
    /// Note that it is an error to call `Fiber::make_active()` within `func`. Doing so will cause
    /// the `Mutex` guard on the instance to never unlock, making the scheduler instance
    /// inaccessible. All standalone functions that access the scheduler and wish to switch fibers
    /// should use `Scheduler::next()` to return the next fiber from `with()` and then call
    /// `make_active()` *after* `with()` has returned.
    fn with<F, T>(func: F) -> T
        where F: FnOnce(&mut Scheduler) -> T
    {
        INSTANCE_INIT.call_once(|| {
            let scheduler = Scheduler {
                current_work: HashSet::new(),
                work_map: HashMap::new(),
                new_work: VecDeque::new(),
                ready_fibers: VecDeque::new(),
                dependencies: HashMap::new(),
                finished: VecDeque::new(),
            };

            INSTANCE.init(Mutex::new(scheduler));
            CONDVAR.init(Condvar::new());
        });

        let instance = INSTANCE.borrow();

        let mut guard = instance.lock().expect("Scheduler mutex was poisoned");
        func(&mut *guard)
    }

    /// Add a new unit of work to the pending queue.
    fn schedule_work(&mut self, work: Work) {
        assert!(self.current_work.insert(work.id), "Work's ID was already present in current work set");
        self.new_work.push_back(work);
        CONDVAR.borrow().notify_one();
    }

    /// Adds `dependency` as a dependency of the currently running fiber.
    ///
    /// Returns `true` if work is still in progress and was added as a dependency, false otherwise.
    fn add_dependency(&mut self, dependency: WorkId) -> bool {
        let pending = fiber::current().unwrap();

        if self.current_work.contains(&dependency) {
            debug_assert!(
                !self.dependencies.contains_key(&pending),
                "Marking a fiber as pending but it is already pending: {:?}",
                pending,
            );

            // Add `pending` to set of pending fibers and list `dependencies` as dependencies.
            let &mut (_, ref mut dependencies_set) =
            self.dependencies
            .entry(pending)
            .or_insert((None, HashSet::new()));

            // Add `fibers` to the list of ready fibers.
            dependencies_set.insert(dependency);

            true
        } else {
            false
        }
    }

    fn start_work(&mut self, new_work: WorkId) {
        debug_assert!(self.current_work.contains(&new_work), "Work ID was not in current work set");

        let current = fiber::current().unwrap();
        self.work_map.insert(current, new_work);
    }

    /// Removes the specified unit of work from the scheduler, updating any dependent work.
    fn finish_work(&mut self, finished_work: WorkId) {
        // Iterate over all suspended work units, removing `finished_work` as a dependency where
        // necessary. If any of the work units no longer have dependencies then
        let mut ready = Vec::new();
        for (&pending_fiber, &mut (_, ref mut dependencies)) in &mut self.dependencies {
            dependencies.remove(&finished_work);
            if dependencies.len() == 0 {
                ready.push(pending_fiber);
            }
        }

        for ready_work in ready {
            let (maybe_fiber, _) = self.dependencies.remove(&ready_work).unwrap();
            if let Some(ready_fiber) = maybe_fiber {
                self.ready_fibers.push_back(ready_fiber);
                CONDVAR.borrow().notify_one();
            }
        }

        let fiber = fiber::current().unwrap();
        assert!(self.current_work.remove(&finished_work), "{:?} wasn't in current work set when it finished", finished_work);
        assert!(self.work_map.remove(&fiber).is_some(), "{:?} didn't have {:?} associated in the work map", fiber, finished_work);
    }

    /// Performs the necessary bookkeeping when a fiber becomes active.
    fn handle_suspended(&mut self, suspended: Fiber) {
        // If the suspended fiber has dependencies then update the dependencies map with the
        // actual fiber, that way when its dependencies complete it can be resumed. Otherwise, the
        // fiber is done and ready to take on more work. This means that we need to make sure that
        // we always call `add_dependencies()` before suspending a fiber, otherwise a fiber could
        // be marked as done before it's ready.
        if let Some(&mut (ref mut none_fiber, _)) = self.dependencies.get_mut(&suspended.id()) {
            debug_assert!(none_fiber.is_none(), "Dependencies map already had a fiber assicated with fiber ID");
            mem::replace(none_fiber, Some(suspended));
        } else if self.work_map.contains_key(&suspended.id()) {
            self.ready_fibers.push_back(suspended);
        } else {
            self.finished.push_back(suspended);
        }
    }

    /// Gets the next ready fiber, or creates a new one if necessary.
    fn next_fiber(&mut self) -> Fiber {
        self.ready_fibers.pop_front()
            .or_else(|| self.finished.pop_front())
            .unwrap_or_else(|| {
                fn fiber_proc(prev: Fiber) -> ! {
                    // The current fiber has been resumed. Let the scheduler know that the previous fiber is no
                    // longer active.
                    Scheduler::with(|scheduler| scheduler.handle_suspended(prev));

                    fiber_routine();
                }

                Fiber::new(DEFAULT_STACK_SIZE, fiber_proc)
            })
    }

    /// Gets the next available work for a thread, either a new unit of work or a ready fiber.
    ///
    /// Prioritizes new work over pending fibers, and will only return ready fibers that already
    /// have work. To get *any* next fiber, including ones without active work or a new one if no
    /// existing fibers are available, use `next_fiber()`.
    fn next(&mut self) -> Option<NextWork> {
        if let Some(work) = self.new_work.pop_front() {
            Some(NextWork::Work(work))
        } else {
            self.ready_fibers.pop_front().map(|fiber| NextWork::Fiber(fiber))
        }
    }
}
<commit_msg>Don't leak memory when awaiting an Async<T><commit_after>//! The main scheduler logic.
//!
//! The scheduler is implemented as a singleton in order to make it easy for code anywhere in the
//! project to make use of async functionality. The actual scheduler instance is not publicly
//! accessible, instead we use various standalone functions like `start()` and `wait_for()` to
//! safely manage access to the scheduler.
//!
//! # Scheduling Work
//!
//! Use `scheduler::start()` to run some work asynchronously, getting an `Async<T>` representing the
//! result of the work. Use `Async::await()` to suspend the current fiber until the work completes
//! and get the result. By default, dropping an `Async<T>` will suspend the current fiber until
//! the work finishes, but you can use `Async::forget()` to ignore the result without blocking.
//!
//! # Sharing Data Between Work
//!
//! Unlike with `std::thread::spawn()`, it's possible for work started with `scheduler::start()`
//! to borrow data from the caller:
//!
//! ```
//!
//! ```

use fiber::{self, Fiber, FiberId};
use cell_extras::AtomicInitCell;
use std::boxed::FnBox;
use std::collections::{HashMap, HashSet, VecDeque};
use std::fmt::{self, Debug, Formatter};
use std::marker::PhantomData;
use std::mem;
use std::sync::{Condvar, Mutex, Once, ONCE_INIT};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::{self, Receiver};

const DEFAULT_STACK_SIZE: usize = 64 * 1024;

static CONDVAR: AtomicInitCell<Condvar> = AtomicInitCell::new();
static INSTANCE: AtomicInitCell<Mutex<Scheduler>> = AtomicInitCell::new();
static INSTANCE_INIT: Once = ONCE_INIT;
static WORK_COUNTER: AtomicUsize = AtomicUsize::new(1);

/// Represents the result of a computation that may finish at some point in the future.
///
/// Use `scheduler::start()` to run some work asynchronously, getting an `Async<T>` representing the
/// result of the work. Use `Async::await()` to suspend the current fiber until the work completes
/// and get the result. By default, dropping an `Async<T>` will suspend the current fiber until
/// the work finishes, but you can use `Async::forget()` to ignore the result without blocking.
///
/// # Sharing Data Across Work
///
/// It's possible to share
#[derive(Debug)]
pub struct Async<'a, T> {
    work: WorkId,
    receiver: Receiver<T>,
    _phantom: PhantomData<&'a FnMut()>,
}

impl<'a, T> Async<'a, T> {
    /// Suspend the current fiber until the async operation finishes.
    pub fn await(self) -> T {
        let result = {
            let Async { work, ref receiver, .. } = self;
            work.await();
            receiver.try_recv().expect("Failed to receive result of async computation")
        };

        result
    }

    pub fn work_id(&self) -> WorkId {
        self.work
    }
}

impl<T> Async<'static, T> {
    pub fn forget(self) {
        mem::forget(self);
    }
}

impl<'a, T> Drop for Async<'a, T> {
    fn drop(&mut self) {
        self.work.await();
    }
}

/// A shareable reference to a work unit, counterpart to `Async<T>`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WorkId(usize);

impl WorkId {
    /// Suspends the current fiber until this work unit has completed.
    ///
    /// If the work unit has already finished then `await()` will return immediately.
    pub fn await(self) {
        if Scheduler::with(|scheduler| scheduler.add_dependency(self)) {
            suspend();
        }
    }
}

/// Initializes a newly-spawned worker thread.
///
/// Prepares the worker thread by initializing it for Fiber usage.
// TODO: This should probably only be public within the crate. Only the engine should be using this,
// and only at startup, we probably don't want user code to be spawning threads anyway.
pub fn init_thread() {
    // Make sure the scheduler is initialized before first use.
    Scheduler::with(|_| {});

    // Manually convert current thread into a fiber because reasons.
    fiber::init();
}

// TODO: This should probably only be public within the crate. Only the engine should be using this,
// and only at startup, we probably don't want user code to be spawning threads anyway.
pub fn run_wait_fiber() {
    // Make sure the scheduler is initialized before first use.
    Scheduler::with(|_| {});

    // Setup this thread for running fibers and create an initial fiber for it. This will become
    // the wait fiber for this thread.
    fiber::init();

    fiber_routine();
}

pub fn start<'a, F, T>(func: F) -> Async<'a, T>
    where
    F: FnOnce() -> T,
    F: 'a + Send,
    T: 'a + Send,
{
    // Normally we can't box a closure with a non 'static lifetime because it could outlive its
    // borrowed data. In this case the lifetime parameter on the returned `Async` ensures that
    // the closure can't outlive the borrowed data, so we use this evil magic to convince the
    // compiler to allow us to box the closure.
    unsafe fn erase_lifetime<'a, F>(func: F) -> Box<FnBox()>
        where
        F: FnOnce(),
        F: 'a + Send,
    {
        let boxed_proc = Box::new(func);
        let proc_ptr = Box::into_raw(boxed_proc) as *mut FnBox();
        Box::from_raw(::std::mem::transmute(proc_ptr))
    }

    // Create the channel that'll be used to send the result of the operation to the `Async` object.
    let (sender, receiver) = mpsc::sync_channel(1);

    let work_id = WorkId(WORK_COUNTER.fetch_add(1, Ordering::Relaxed));

    let work_proc = unsafe {
        erase_lifetime(move || {
            let result = func();
            sender.try_send(result).expect("Failed to send async result");
        })
    };

    Scheduler::with(move |scheduler| scheduler.schedule_work(Work {
        func: work_proc,
        id: work_id,
    }));

    Async {
        work: work_id,
        receiver: receiver,
        _phantom: PhantomData,
    }
}

/// Suspends the current fiber and makes the wait fiber active.
///
/// Generally you shouldn't need to call this directly, but if you have one piece of code that
/// runs synchronously for a long time you can use `suspend()` to yield time to other work.
pub fn suspend() {
    let next_fiber = Scheduler::with(|scheduler| scheduler.next_fiber());

    let suspended = unsafe { next_fiber.resume() };

    Scheduler::with(move |scheduler| scheduler.handle_suspended(suspended));
}

fn fiber_routine() -> ! {
    loop {
        match Scheduler::with(|scheduler| scheduler.next()) {
            Some(NextWork::Work(Work { func, id })) => {
                Scheduler::with(|scheduler| scheduler.start_work(id));
                func();
                Scheduler::with(|scheduler| scheduler.finish_work(id));
            },
            Some(NextWork::Fiber(fiber)) => {
                let suspended = unsafe { fiber.resume() };
                Scheduler::with(move |scheduler| scheduler.handle_suspended(suspended));
            },
            None => {
                // If there's no new work and no fibers ready to run then we want to block the
                // thread until some becomes available.
                let mutex = INSTANCE.borrow();
                let condvar = CONDVAR.borrow();

                let _ = condvar
                    .wait(mutex.lock().expect("Scheduler mutex was poisoned"))
                    .expect("Scheduler mutex was poisoned");
            },
        }
    }
}

struct Work {
    func: Box<FnBox()>,
    id: WorkId,
}

impl Debug for Work {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> {
        write!(formatter, "Work {{ id: {:?} }}", self.id)
    }
}

enum NextWork {
    Work(Work),
    Fiber(Fiber),
}

struct Scheduler {
    /// Work units that are currently pending or in progress.
    current_work: HashSet<WorkId>,

    work_map: HashMap<FiberId, WorkId>,

    /// New units of work that haven't been started on a fiber yet.
    ///
    /// These are ready to be made active at any time.
    new_work: VecDeque<Work>,

    /// Fibers that have no pending dependencies.
    ///
    /// These are ready to be made active at any time.
    ready_fibers: VecDeque<Fiber>,

    /// A map specifying which pending fibers depend on which others.
    ///
    /// Once all of a fiber's dependencies complete it should be moved to `new_work`.
    dependencies: HashMap<FiberId, (Option<Fiber>, HashSet<WorkId>)>,

    // TODO: Should we distinguise between "finished" and "ready" fibers? My intuition is that we'd
    // want to give fibers that actively have work CPU time before we resume fibers that would be
    // pulling new work, but maybe not? If we threw them all into one queue I guess the worst case
    // scenario would be there's no work left, a bunch of empty fibers, and only a few fibers with
    // active work. In which case we might have to cycle through a bunch of fibers before we can
    // start doing actual work.
    finished: VecDeque<Fiber>,
}

unsafe impl Send for Scheduler {}

impl Scheduler {
    /// Provides safe access to the scheduler instance.
    ///
    /// # Fiber Switches
    ///
    /// Note that it is an error to call `Fiber::make_active()` within `func`. Doing so will cause
    /// the `Mutex` guard on the instance to never unlock, making the scheduler instance
    /// inaccessible. All standalone functions that access the scheduler and wish to switch fibers
    /// should use `Scheduler::next()` to return the next fiber from `with()` and then call
    /// `make_active()` *after* `with()` has returned.
    fn with<F, T>(func: F) -> T
        where F: FnOnce(&mut Scheduler) -> T
    {
        INSTANCE_INIT.call_once(|| {
            let scheduler = Scheduler {
                current_work: HashSet::new(),
                work_map: HashMap::new(),
                new_work: VecDeque::new(),
                ready_fibers: VecDeque::new(),
                dependencies: HashMap::new(),
                finished: VecDeque::new(),
            };

            INSTANCE.init(Mutex::new(scheduler));
            CONDVAR.init(Condvar::new());
        });

        let instance = INSTANCE.borrow();

        let mut guard = instance.lock().expect("Scheduler mutex was poisoned");
        func(&mut *guard)
    }

    /// Add a new unit of work to the pending queue.
    fn schedule_work(&mut self, work: Work) {
        assert!(self.current_work.insert(work.id), "Work's ID was already present in current work set");
        self.new_work.push_back(work);
        CONDVAR.borrow().notify_one();
    }

    /// Adds `dependency` as a dependency of the currently running fiber.
    ///
    /// Returns `true` if work is still in progress and was added as a dependency, false otherwise.
    fn add_dependency(&mut self, dependency: WorkId) -> bool {
        let pending = fiber::current().unwrap();

        if self.current_work.contains(&dependency) {
            debug_assert!(
                !self.dependencies.contains_key(&pending),
                "Marking a fiber as pending but it is already pending: {:?}",
                pending,
            );

            // Add `pending` to set of pending fibers and list `dependencies` as dependencies.
            let &mut (_, ref mut dependencies_set) =
            self.dependencies
            .entry(pending)
            .or_insert((None, HashSet::new()));

            // Add `fibers` to the list of ready fibers.
            dependencies_set.insert(dependency);

            true
        } else {
            false
        }
    }

    fn start_work(&mut self, new_work: WorkId) {
        debug_assert!(self.current_work.contains(&new_work), "Work ID was not in current work set");

        let current = fiber::current().unwrap();
        self.work_map.insert(current, new_work);
    }

    /// Removes the specified unit of work from the scheduler, updating any dependent work.
    fn finish_work(&mut self, finished_work: WorkId) {
        // Iterate over all suspended work units, removing `finished_work` as a dependency where
        // necessary. If any of the work units no longer have dependencies then
        let mut ready = Vec::new();
        for (&pending_fiber, &mut (_, ref mut dependencies)) in &mut self.dependencies {
            dependencies.remove(&finished_work);
            if dependencies.len() == 0 {
                ready.push(pending_fiber);
            }
        }

        for ready_work in ready {
            let (maybe_fiber, _) = self.dependencies.remove(&ready_work).unwrap();
            if let Some(ready_fiber) = maybe_fiber {
                self.ready_fibers.push_back(ready_fiber);
                CONDVAR.borrow().notify_one();
            }
        }

        let fiber = fiber::current().unwrap();
        assert!(self.current_work.remove(&finished_work), "{:?} wasn't in current work set when it finished", finished_work);
        assert!(self.work_map.remove(&fiber).is_some(), "{:?} didn't have {:?} associated in the work map", fiber, finished_work);
    }

    /// Performs the necessary bookkeeping when a fiber becomes active.
    fn handle_suspended(&mut self, suspended: Fiber) {
        // If the suspended fiber has dependencies then update the dependencies map with the
        // actual fiber, that way when its dependencies complete it can be resumed. Otherwise, the
        // fiber is done and ready to take on more work. This means that we need to make sure that
        // we always call `add_dependencies()` before suspending a fiber, otherwise a fiber could
        // be marked as done before it's ready.
        if let Some(&mut (ref mut none_fiber, _)) = self.dependencies.get_mut(&suspended.id()) {
            debug_assert!(none_fiber.is_none(), "Dependencies map already had a fiber assicated with fiber ID");
            mem::replace(none_fiber, Some(suspended));
        } else if self.work_map.contains_key(&suspended.id()) {
            self.ready_fibers.push_back(suspended);
        } else {
            self.finished.push_back(suspended);
        }
    }

    /// Gets the next ready fiber, or creates a new one if necessary.
    fn next_fiber(&mut self) -> Fiber {
        self.ready_fibers.pop_front()
            .or_else(|| self.finished.pop_front())
            .unwrap_or_else(|| {
                fn fiber_proc(prev: Fiber) -> ! {
                    // The current fiber has been resumed. Let the scheduler know that the previous fiber is no
                    // longer active.
                    Scheduler::with(|scheduler| scheduler.handle_suspended(prev));

                    fiber_routine();
                }

                Fiber::new(DEFAULT_STACK_SIZE, fiber_proc)
            })
    }

    /// Gets the next available work for a thread, either a new unit of work or a ready fiber.
    ///
    /// Prioritizes new work over pending fibers, and will only return ready fibers that already
    /// have work. To get *any* next fiber, including ones without active work or a new one if no
    /// existing fibers are available, use `next_fiber()`.
    fn next(&mut self) -> Option<NextWork> {
        if let Some(work) = self.new_work.pop_front() {
            Some(NextWork::Work(work))
        } else {
            self.ready_fibers.pop_front().map(|fiber| NextWork::Fiber(fiber))
        }
    }
}
<|endoftext|>


<commit_before>//! The `filter` element.

use cssparser::Parser;
use markup5ever::{expanded_name, local_name, namespace_url, ns};

use crate::allowed_url::Fragment;
use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{Draw, Element, ElementResult, SetAttributes};
use crate::error::ValueErrorKind;
use crate::iri::IRI;
use crate::length::*;
use crate::node::{Node, NodeBorrow};
use crate::parsers::{Parse, ParseValue};
use crate::properties::ComputedValues;
use crate::property_bag::PropertyBag;
use crate::rect::Rect;
use crate::transform::Transform;

/// The <filter> node.
pub struct Filter {
    x: Length<Horizontal>,
    y: Length<Vertical>,
    width: Length<Horizontal>,
    height: Length<Vertical>,
    filterunits: CoordUnits,
    primitiveunits: CoordUnits,
}

impl Default for Filter {
    /// Constructs a new `Filter` with default properties.
    fn default() -> Self {
        Self {
            x: Length::<Horizontal>::parse_str("-10%").unwrap(),
            y: Length::<Vertical>::parse_str("-10%").unwrap(),
            width: Length::<Horizontal>::parse_str("120%").unwrap(),
            height: Length::<Vertical>::parse_str("120%").unwrap(),
            filterunits: CoordUnits::ObjectBoundingBox,
            primitiveunits: CoordUnits::UserSpaceOnUse,
        }
    }
}

impl Filter {
    pub fn get_filter_units(&self) -> CoordUnits {
        self.filterunits
    }

    pub fn get_primitive_units(&self) -> CoordUnits {
        self.primitiveunits
    }

    /// Computes and returns the filter effects region.
    pub fn compute_effects_region(
        &self,
        computed_from_target_node: &ComputedValues,
        draw_ctx: &mut DrawingCtx,
        transform: Transform,
        width: f64,
        height: f64,
    ) -> BoundingBox {
        // Filters use the properties of the target node.
        let values = computed_from_target_node;

        let mut bbox = BoundingBox::new();

        // transform is set up in FilterContext::new() in such a way that for
        // filterunits == ObjectBoundingBox it includes scaling to correct width, height and
        // this is why width and height are set to 1, 1 (and for filterunits ==
        // UserSpaceOnUse, transform doesn't include scaling because in this case the correct
        // width, height already happens to be the viewbox width, height).
        //
        // It's done this way because with ObjectBoundingBox, non-percentage values are supposed to
        // represent the fractions of the referenced node, and with width and height = 1, 1 this
        // works out exactly like that.
        let params = if self.filterunits == CoordUnits::ObjectBoundingBox {
            draw_ctx.push_view_box(1.0, 1.0)
        } else {
            draw_ctx.get_view_params()
        };

        // With filterunits == ObjectBoundingBox, lengths represent fractions or percentages of the
        // referencing node. No units are allowed (it's checked during attribute parsing).
        let (x, y, w, h) = if self.filterunits == CoordUnits::ObjectBoundingBox {
            (
                self.x.length,
                self.y.length,
                self.width.length,
                self.height.length,
            )
        } else {
            (
                self.x.normalize(values, &params),
                self.y.normalize(values, &params),
                self.width.normalize(values, &params),
                self.height.normalize(values, &params),
            )
        };

        let rect = Rect::new(x, y, x + w, y + h);
        let other_bbox = BoundingBox::new().with_transform(transform).with_rect(rect);

        // At this point all of the previous viewbox and matrix business gets converted to pixel
        // coordinates in the final surface, because bbox is created with an identity transform.
        bbox.insert(&other_bbox);

        // Finally, clip to the width and height of our surface.
        let rect = Rect::from_size(width, height);
        let other_bbox = BoundingBox::new().with_rect(rect);
        bbox.clip(&other_bbox);

        bbox
    }
}

impl SetAttributes for Filter {
    fn set_attributes(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
        // Parse filterUnits first as it affects x, y, width, height checks.
        let result = pbag
            .iter()
            .find(|(attr, _)| attr.expanded() == expanded_name!("", "filterUnits"))
            .and_then(|(attr, value)| attr.parse(value).ok());
        if let Some(filter_units) = result {
            self.filterunits = filter_units
        }

        // With ObjectBoundingBox, only fractions and percents are allowed.
        let no_units_allowed = self.filterunits == CoordUnits::ObjectBoundingBox;

        let check_units_horizontal = |length: Length<Horizontal>| {
            if !no_units_allowed {
                return Ok(length);
            }

            match length.unit {
                LengthUnit::Px | LengthUnit::Percent => Ok(length),
                _ => Err(ValueErrorKind::parse_error(
                    "unit identifiers are not allowed with filterUnits set to objectBoundingBox",
                )),
            }
        };

        let check_units_vertical = |length: Length<Vertical>| {
            if !no_units_allowed {
                return Ok(length);
            }

            match length.unit {
                LengthUnit::Px | LengthUnit::Percent => Ok(length),
                _ => Err(ValueErrorKind::parse_error(
                    "unit identifiers are not allowed with filterUnits set to objectBoundingBox",
                )),
            }
        };

        let check_units_horizontal_and_ensure_nonnegative = |length: Length<Horizontal>| {
            check_units_horizontal(length).and_then(Length::<Horizontal>::check_nonnegative)
        };

        let check_units_vertical_and_ensure_nonnegative = |length: Length<Vertical>| {
            check_units_vertical(length).and_then(Length::<Vertical>::check_nonnegative)
        };

        // Parse the rest of the attributes.
        for (attr, value) in pbag.iter() {
            match attr.expanded() {
                expanded_name!("", "x") => {
                    self.x = attr.parse_and_validate(value, check_units_horizontal)?
                }
                expanded_name!("", "y") => {
                    self.y = attr.parse_and_validate(value, check_units_vertical)?
                }
                expanded_name!("", "width") => {
                    self.width = attr
                        .parse_and_validate(value, check_units_horizontal_and_ensure_nonnegative)?
                }
                expanded_name!("", "height") => {
                    self.height =
                        attr.parse_and_validate(value, check_units_vertical_and_ensure_nonnegative)?
                }
                expanded_name!("", "primitiveUnits") => self.primitiveunits = attr.parse(value)?,
                _ => (),
            }
        }

        Ok(())
    }
}

impl Draw for Filter {}

#[derive(Debug, Clone, PartialEq)]
pub enum FilterValue {
    URL(Fragment),
}
#[derive(Debug, Clone, PartialEq)]
pub struct FilterValueList(pub Vec<FilterValue>);

impl Default for FilterValueList {
    fn default() -> FilterValueList {
        FilterValueList(vec![])
    }
}

impl FilterValueList {
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Check that at least one filter URI exists and that all contained
    /// URIs reference existing <filter> elements.
    pub fn is_applicable(&self, node: &Node, acquired_nodes: &mut AcquiredNodes) -> bool {
        if self.is_empty() {
            return false;
        }

        self.0.iter().all(|filter| match filter {
            FilterValue::URL(filter_uri) => match acquired_nodes.acquire(filter_uri) {
                Ok(acquired) => {
                    let filter_node = acquired.get();

                    match *filter_node.borrow_element() {
                        Element::Filter(_) => true,
                        _ => {
                            rsvg_log!(
                                "element {} will not be filtered since \"{}\" is not a filter",
                                node,
                                filter_uri,
                            );
                            false
                        }
                    }
                }
                _ => {
                    rsvg_log!(
                        "element {} will not be filtered since its filter \"{}\" was not found",
                        node,
                        filter_uri,
                    );
                    false
                }
            },
        })
    }
}

impl Parse for FilterValueList {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, crate::error::ParseError<'i>> {
        let mut result = FilterValueList::default();

        if parser
            .try_parse(|p| p.expect_ident_matching("none"))
            .is_ok()
        {
            return Ok(result);
        }

        while !parser.is_exhausted() {
            let loc = parser.current_source_location();

            if let Ok(IRI::Resource(uri)) = IRI::parse(parser) {
                result.0.push(FilterValue::URL(uri));
            } else {
                let token = parser.next()?;
                return Err(loc.new_basic_unexpected_token_error(token.clone()).into());
            }
        }

        Ok(result)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parses_filter_value_list() {
        assert_eq!(
            FilterValueList::parse_str("none"),
            Ok(FilterValueList::default())
        );

        let f1 = Fragment::new(Some("foo.svg".to_string()), "bar".to_string());
        let f2 = Fragment::new(Some("test.svg".to_string()), "baz".to_string());
        assert_eq!(
            FilterValueList::parse_str("url(foo.svg#bar) url(test.svg#baz)"),
            Ok(FilterValueList(vec![
                FilterValue::URL(f1),
                FilterValue::URL(f2)
            ]))
        );
    }

    #[test]
    fn detects_invalid_filter_value_list() {
        assert!(FilterValueList::parse_str("fail").is_err());
        assert!(FilterValueList::parse_str("url(#test) none").is_err());
    }
}
<commit_msg>Detect empty strings as invalid in FilterValueList::parse<commit_after>//! The `filter` element.

use cssparser::Parser;
use markup5ever::{expanded_name, local_name, namespace_url, ns};

use crate::allowed_url::Fragment;
use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{Draw, Element, ElementResult, SetAttributes};
use crate::error::ValueErrorKind;
use crate::iri::IRI;
use crate::length::*;
use crate::node::{Node, NodeBorrow};
use crate::parsers::{Parse, ParseValue};
use crate::properties::ComputedValues;
use crate::property_bag::PropertyBag;
use crate::rect::Rect;
use crate::transform::Transform;

/// The <filter> node.
pub struct Filter {
    x: Length<Horizontal>,
    y: Length<Vertical>,
    width: Length<Horizontal>,
    height: Length<Vertical>,
    filterunits: CoordUnits,
    primitiveunits: CoordUnits,
}

impl Default for Filter {
    /// Constructs a new `Filter` with default properties.
    fn default() -> Self {
        Self {
            x: Length::<Horizontal>::parse_str("-10%").unwrap(),
            y: Length::<Vertical>::parse_str("-10%").unwrap(),
            width: Length::<Horizontal>::parse_str("120%").unwrap(),
            height: Length::<Vertical>::parse_str("120%").unwrap(),
            filterunits: CoordUnits::ObjectBoundingBox,
            primitiveunits: CoordUnits::UserSpaceOnUse,
        }
    }
}

impl Filter {
    pub fn get_filter_units(&self) -> CoordUnits {
        self.filterunits
    }

    pub fn get_primitive_units(&self) -> CoordUnits {
        self.primitiveunits
    }

    /// Computes and returns the filter effects region.
    pub fn compute_effects_region(
        &self,
        computed_from_target_node: &ComputedValues,
        draw_ctx: &mut DrawingCtx,
        transform: Transform,
        width: f64,
        height: f64,
    ) -> BoundingBox {
        // Filters use the properties of the target node.
        let values = computed_from_target_node;

        let mut bbox = BoundingBox::new();

        // transform is set up in FilterContext::new() in such a way that for
        // filterunits == ObjectBoundingBox it includes scaling to correct width, height and
        // this is why width and height are set to 1, 1 (and for filterunits ==
        // UserSpaceOnUse, transform doesn't include scaling because in this case the correct
        // width, height already happens to be the viewbox width, height).
        //
        // It's done this way because with ObjectBoundingBox, non-percentage values are supposed to
        // represent the fractions of the referenced node, and with width and height = 1, 1 this
        // works out exactly like that.
        let params = if self.filterunits == CoordUnits::ObjectBoundingBox {
            draw_ctx.push_view_box(1.0, 1.0)
        } else {
            draw_ctx.get_view_params()
        };

        // With filterunits == ObjectBoundingBox, lengths represent fractions or percentages of the
        // referencing node. No units are allowed (it's checked during attribute parsing).
        let (x, y, w, h) = if self.filterunits == CoordUnits::ObjectBoundingBox {
            (
                self.x.length,
                self.y.length,
                self.width.length,
                self.height.length,
            )
        } else {
            (
                self.x.normalize(values, &params),
                self.y.normalize(values, &params),
                self.width.normalize(values, &params),
                self.height.normalize(values, &params),
            )
        };

        let rect = Rect::new(x, y, x + w, y + h);
        let other_bbox = BoundingBox::new().with_transform(transform).with_rect(rect);

        // At this point all of the previous viewbox and matrix business gets converted to pixel
        // coordinates in the final surface, because bbox is created with an identity transform.
        bbox.insert(&other_bbox);

        // Finally, clip to the width and height of our surface.
        let rect = Rect::from_size(width, height);
        let other_bbox = BoundingBox::new().with_rect(rect);
        bbox.clip(&other_bbox);

        bbox
    }
}

impl SetAttributes for Filter {
    fn set_attributes(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
        // Parse filterUnits first as it affects x, y, width, height checks.
        let result = pbag
            .iter()
            .find(|(attr, _)| attr.expanded() == expanded_name!("", "filterUnits"))
            .and_then(|(attr, value)| attr.parse(value).ok());
        if let Some(filter_units) = result {
            self.filterunits = filter_units
        }

        // With ObjectBoundingBox, only fractions and percents are allowed.
        let no_units_allowed = self.filterunits == CoordUnits::ObjectBoundingBox;

        let check_units_horizontal = |length: Length<Horizontal>| {
            if !no_units_allowed {
                return Ok(length);
            }

            match length.unit {
                LengthUnit::Px | LengthUnit::Percent => Ok(length),
                _ => Err(ValueErrorKind::parse_error(
                    "unit identifiers are not allowed with filterUnits set to objectBoundingBox",
                )),
            }
        };

        let check_units_vertical = |length: Length<Vertical>| {
            if !no_units_allowed {
                return Ok(length);
            }

            match length.unit {
                LengthUnit::Px | LengthUnit::Percent => Ok(length),
                _ => Err(ValueErrorKind::parse_error(
                    "unit identifiers are not allowed with filterUnits set to objectBoundingBox",
                )),
            }
        };

        let check_units_horizontal_and_ensure_nonnegative = |length: Length<Horizontal>| {
            check_units_horizontal(length).and_then(Length::<Horizontal>::check_nonnegative)
        };

        let check_units_vertical_and_ensure_nonnegative = |length: Length<Vertical>| {
            check_units_vertical(length).and_then(Length::<Vertical>::check_nonnegative)
        };

        // Parse the rest of the attributes.
        for (attr, value) in pbag.iter() {
            match attr.expanded() {
                expanded_name!("", "x") => {
                    self.x = attr.parse_and_validate(value, check_units_horizontal)?
                }
                expanded_name!("", "y") => {
                    self.y = attr.parse_and_validate(value, check_units_vertical)?
                }
                expanded_name!("", "width") => {
                    self.width = attr
                        .parse_and_validate(value, check_units_horizontal_and_ensure_nonnegative)?
                }
                expanded_name!("", "height") => {
                    self.height =
                        attr.parse_and_validate(value, check_units_vertical_and_ensure_nonnegative)?
                }
                expanded_name!("", "primitiveUnits") => self.primitiveunits = attr.parse(value)?,
                _ => (),
            }
        }

        Ok(())
    }
}

impl Draw for Filter {}

#[derive(Debug, Clone, PartialEq)]
pub enum FilterValue {
    URL(Fragment),
}
#[derive(Debug, Clone, PartialEq)]
pub struct FilterValueList(pub Vec<FilterValue>);

impl Default for FilterValueList {
    fn default() -> FilterValueList {
        FilterValueList(vec![])
    }
}

impl FilterValueList {
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Check that at least one filter URI exists and that all contained
    /// URIs reference existing <filter> elements.
    pub fn is_applicable(&self, node: &Node, acquired_nodes: &mut AcquiredNodes) -> bool {
        if self.is_empty() {
            return false;
        }

        self.0.iter().all(|filter| match filter {
            FilterValue::URL(filter_uri) => match acquired_nodes.acquire(filter_uri) {
                Ok(acquired) => {
                    let filter_node = acquired.get();

                    match *filter_node.borrow_element() {
                        Element::Filter(_) => true,
                        _ => {
                            rsvg_log!(
                                "element {} will not be filtered since \"{}\" is not a filter",
                                node,
                                filter_uri,
                            );
                            false
                        }
                    }
                }
                _ => {
                    rsvg_log!(
                        "element {} will not be filtered since its filter \"{}\" was not found",
                        node,
                        filter_uri,
                    );
                    false
                }
            },
        })
    }
}

impl Parse for FilterValueList {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, crate::error::ParseError<'i>> {
        let mut result = FilterValueList::default();

        if parser
            .try_parse(|p| p.expect_ident_matching("none"))
            .is_ok()
        {
            return Ok(result);
        }

        loop {
            let state = parser.state();

            if let Ok(IRI::Resource(uri)) = parser.try_parse(|p| IRI::parse(p)) {
                result.0.push(FilterValue::URL(uri));
            } else {
                parser.reset(&state);
                let token = parser.next()?.clone();
                return Err(parser.new_basic_unexpected_token_error(token).into());
            }

            if parser.is_exhausted() {
                break;
            }
        }

        Ok(result)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parses_filter_value_list() {
        assert_eq!(
            FilterValueList::parse_str("none"),
            Ok(FilterValueList::default())
        );

        let f1 = Fragment::new(Some("foo.svg".to_string()), "bar".to_string());
        let f2 = Fragment::new(Some("test.svg".to_string()), "baz".to_string());
        assert_eq!(
            FilterValueList::parse_str("url(foo.svg#bar) url(test.svg#baz)"),
            Ok(FilterValueList(vec![
                FilterValue::URL(f1),
                FilterValue::URL(f2)
            ]))
        );
    }

    #[test]
    fn detects_invalid_filter_value_list() {
        assert!(FilterValueList::parse_str("").is_err());
        assert!(FilterValueList::parse_str("fail").is_err());
        assert!(FilterValueList::parse_str("url(#test) none").is_err());
    }
}
<|endoftext|>