<commit_before>// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#[macro_use]
extern crate error_chain;
extern crate ordered_float;
extern crate rusqlite;

extern crate mentat_core;

use std::rc::Rc;

use std::collections::HashMap;

use ordered_float::OrderedFloat;

use mentat_core::{
    ToMicros,
    TypedValue,
    Uuid,
};

pub use rusqlite::types::Value;

error_chain! {
    types {
        Error, ErrorKind, ResultExt, Result;
    }

    errors {
        InvalidParameterName(name: String) {
            description("invalid parameter name")
            display("invalid parameter name: '{}'", name)
        }

        BindParamCouldBeGenerated(name: String) {
            description("parameter name could be generated")
            display("parameter name could be generated: '{}'", name)
        }
    }
}

pub type BuildQueryResult = Result<()>;

/// We want to accumulate values that will later be substituted into a SQL statement execution.
/// This struct encapsulates the generated string and the _initial_ argument list.
/// Additional user-supplied argument bindings, with their placeholders accumulated via
/// `push_bind_param`, will be appended to this argument list.
pub struct SQLQuery {
    pub sql: String,

    /// These will eventually perhaps be rusqlite `ToSql` instances.
    pub args: Vec<(String, Rc<rusqlite::types::Value>)>,
}

/// Gratefully based on Diesel's QueryBuilder trait:
/// https://github.com/diesel-rs/diesel/blob/4885f61b8205f7f3c2cfa03837ed6714831abe6b/diesel/src/query_builder/mod.rs#L56
pub trait QueryBuilder {
    fn push_sql(&mut self, sql: &str);
    fn push_identifier(&mut self, identifier: &str) -> BuildQueryResult;
    fn push_typed_value(&mut self, value: &TypedValue) -> BuildQueryResult;
    fn push_bind_param(&mut self, name: &str) -> BuildQueryResult;
    fn finish(self) -> SQLQuery;
}

pub trait QueryFragment {
    fn push_sql(&self, out: &mut QueryBuilder) -> BuildQueryResult;
}

impl QueryFragment for Box<QueryFragment> {
    fn push_sql(&self, out: &mut QueryBuilder) -> BuildQueryResult {
        QueryFragment::push_sql(&**self, out)
    }
}

impl<'a> QueryFragment for &'a QueryFragment {
    fn push_sql(&self, out: &mut QueryBuilder) -> BuildQueryResult {
        QueryFragment::push_sql(&**self, out)
    }
}

impl QueryFragment for () {
    fn push_sql(&self, _out: &mut QueryBuilder) -> BuildQueryResult {
        Ok(())
    }
}

/// A QueryBuilder that implements SQLite's specific escaping rules.
pub struct SQLiteQueryBuilder {
    pub sql: String,

    arg_prefix: String,
    arg_counter: i64,

    // We can't just use an InternSet on the rusqlite::types::Value instances, because that
    // includes f64, so it's not Hash or Eq.
    // Instead we track UUID and String arguments separately, mapping them to their argument name,
    // in order to dedupe. We'll add these to the regular argument vector later.
    uuid_args: HashMap<Rc<Uuid>, String>,            // From value to argument name.
    string_args: HashMap<Rc<String>, String>,        // From value to argument name.
    args: Vec<(String, Rc<rusqlite::types::Value>)>, // (arg, value).
}

impl SQLiteQueryBuilder {
    pub fn new() -> Self {
        SQLiteQueryBuilder::with_prefix("$v".to_string())
    }

    pub fn with_prefix(prefix: String) -> Self {
        SQLiteQueryBuilder {
            sql: String::new(),
            arg_prefix: prefix,
            arg_counter: 0,

            uuid_args: HashMap::default(),
            string_args: HashMap::default(),
            args: vec![],
        }
    }

    fn next_argument_name(&mut self) -> String {
        let arg = format!("{}{}", self.arg_prefix, self.arg_counter);
        self.arg_counter = self.arg_counter + 1;
        arg
    }

    fn push_static_arg(&mut self, val: Rc<rusqlite::types::Value>) {
        // TODO: intern these, too.
        let arg = self.next_argument_name();
        self.push_named_arg(arg.as_str());
        self.args.push((arg, val));
    }

    fn push_named_arg(&mut self, arg: &str) {
        self.push_sql(arg);
    }
}

impl QueryBuilder for SQLiteQueryBuilder {
    fn push_sql(&mut self, sql: &str) {
        self.sql.push_str(sql);
    }

    fn push_identifier(&mut self, identifier: &str) -> BuildQueryResult {
        self.push_sql("`");
        self.push_sql(&identifier.replace("`", "``"));
        self.push_sql("`");
        Ok(())
    }

    fn push_typed_value(&mut self, value: &TypedValue) -> BuildQueryResult {
        use TypedValue::*;
        match value {
            &Ref(entid) => self.push_sql(entid.to_string().as_str()),
            &Boolean(v) => self.push_sql(if v { "1" } else { "0" }),
            &Long(v) => self.push_sql(v.to_string().as_str()),
            &Double(OrderedFloat(v)) => self.push_sql(v.to_string().as_str()),
            &Instant(dt) => {
                self.push_sql(format!("{}", dt.to_micros()).as_str());      // TODO: argument instead?
            },
            &Uuid(ref u) => {
                if let Some(arg) = self.uuid_args.get(u).cloned() {        // Why, borrow checker, why?!
                    self.push_named_arg(arg.as_str());
                } else {
                    let arg = self.next_argument_name();
                    self.push_named_arg(arg.as_str());
                    self.uuid_args.insert(Rc::new(u.clone()), arg);
                }
            },
            // These are both `Rc`. Unfortunately, we can't use that fact when
            // turning these into rusqlite Values.
            // However, we can check to see whether there's an existing var that matches…
            &String(ref s) => {
                if let Some(arg) = self.string_args.get(s).cloned() {
                    self.push_named_arg(arg.as_str());
                } else {
                    let arg = self.next_argument_name();
                    self.push_named_arg(arg.as_str());
                    self.string_args.insert(s.clone(), arg);
                }
            },
            &Keyword(ref s) => {
                // TODO: intern.
                let v = Rc::new(rusqlite::types::Value::Text(s.as_ref().to_string()));
                self.push_static_arg(v);
            },
        }
        Ok(())
    }

    /// Our bind parameters will be interleaved with pushed `TypedValue` instances. That means we
    /// need to use named parameters, not positional parameters.
    /// The `name` argument to this method is expected to be alphanumeric. If not, this method
    /// returns an `InvalidParameterName` error result.
    /// Callers should make sure that the name doesn't overlap with generated parameter names. If
    /// it does, `BindParamCouldBeGenerated` is the error.
    fn push_bind_param(&mut self, name: &str) -> BuildQueryResult {
        // Do some validation first.
        // This is not free, but it's probably worth it for now.
        if !name.chars().all(|c| char::is_alphanumeric(c) || c == '_') {
            bail!(ErrorKind::InvalidParameterName(name.to_string()));
        }

        if name.starts_with(self.arg_prefix.as_str()) &&
           name.chars().skip(self.arg_prefix.len()).all(char::is_numeric) {
               bail!(ErrorKind::BindParamCouldBeGenerated(name.to_string()));
        }

        self.push_sql("$");
        self.push_sql(name);
        Ok(())
    }

    fn finish(self) -> SQLQuery {
        // We collected string and UUID arguments into separate maps so that we could
        // dedupe them. Now we need to turn them into rusqlite Values.
        let mut args = self.args;
        let string_args = self.string_args.into_iter().map(|(val, arg)| {
             (arg, Rc::new(rusqlite::types::Value::Text(val.as_ref().clone())))
        });
        let uuid_args = self.uuid_args.into_iter().map(|(val, arg)| {
            // Get a byte array.
            let bytes = val.as_bytes().clone();
            (arg, Rc::new(rusqlite::types::Value::Blob(bytes.to_vec())))
        });

        args.extend(string_args);
        args.extend(uuid_args);

        // Get the args in the right order -- $v0, $v1…
        args.sort_by(|&(ref k1, _), &(ref k2, _)| k1.cmp(k2));
        SQLQuery {
            sql: self.sql,
            args: args,
        }
    }
}

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

    fn string_arg(s: &str) -> Rc<rusqlite::types::Value> {
        Rc::new(rusqlite::types::Value::Text(s.to_string()))
    }

    #[test]
    fn test_sql() {
        let mut s = SQLiteQueryBuilder::new();
        s.push_sql("SELECT ");
        s.push_identifier("foo").unwrap();
        s.push_sql(" WHERE ");
        s.push_identifier("bar").unwrap();
        s.push_sql(" = ");
        s.push_static_arg(string_arg("frobnicate"));
        s.push_sql(" OR ");
        s.push_static_arg(string_arg("swoogle"));
        let q = s.finish();

        assert_eq!(q.sql.as_str(), "SELECT `foo` WHERE `bar` = $v0 OR $v1");
        assert_eq!(q.args,
                   vec![("$v0".to_string(), string_arg("frobnicate")),
                        ("$v1".to_string(), string_arg("swoogle"))]);
    }
}
<commit_msg>Review comment: generalize from Uuid SQL arguments to byte arrays.<commit_after>// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#[macro_use]
extern crate error_chain;
extern crate ordered_float;
extern crate rusqlite;

extern crate mentat_core;

use std::rc::Rc;

use std::collections::HashMap;

use ordered_float::OrderedFloat;

use mentat_core::{
    ToMicros,
    TypedValue,
};

pub use rusqlite::types::Value;

error_chain! {
    types {
        Error, ErrorKind, ResultExt, Result;
    }

    errors {
        InvalidParameterName(name: String) {
            description("invalid parameter name")
            display("invalid parameter name: '{}'", name)
        }

        BindParamCouldBeGenerated(name: String) {
            description("parameter name could be generated")
            display("parameter name could be generated: '{}'", name)
        }
    }
}

pub type BuildQueryResult = Result<()>;

/// We want to accumulate values that will later be substituted into a SQL statement execution.
/// This struct encapsulates the generated string and the _initial_ argument list.
/// Additional user-supplied argument bindings, with their placeholders accumulated via
/// `push_bind_param`, will be appended to this argument list.
pub struct SQLQuery {
    pub sql: String,

    /// These will eventually perhaps be rusqlite `ToSql` instances.
    pub args: Vec<(String, Rc<rusqlite::types::Value>)>,
}

/// Gratefully based on Diesel's QueryBuilder trait:
/// https://github.com/diesel-rs/diesel/blob/4885f61b8205f7f3c2cfa03837ed6714831abe6b/diesel/src/query_builder/mod.rs#L56
pub trait QueryBuilder {
    fn push_sql(&mut self, sql: &str);
    fn push_identifier(&mut self, identifier: &str) -> BuildQueryResult;
    fn push_typed_value(&mut self, value: &TypedValue) -> BuildQueryResult;
    fn push_bind_param(&mut self, name: &str) -> BuildQueryResult;
    fn finish(self) -> SQLQuery;
}

pub trait QueryFragment {
    fn push_sql(&self, out: &mut QueryBuilder) -> BuildQueryResult;
}

impl QueryFragment for Box<QueryFragment> {
    fn push_sql(&self, out: &mut QueryBuilder) -> BuildQueryResult {
        QueryFragment::push_sql(&**self, out)
    }
}

impl<'a> QueryFragment for &'a QueryFragment {
    fn push_sql(&self, out: &mut QueryBuilder) -> BuildQueryResult {
        QueryFragment::push_sql(&**self, out)
    }
}

impl QueryFragment for () {
    fn push_sql(&self, _out: &mut QueryBuilder) -> BuildQueryResult {
        Ok(())
    }
}

/// A QueryBuilder that implements SQLite's specific escaping rules.
pub struct SQLiteQueryBuilder {
    pub sql: String,

    arg_prefix: String,
    arg_counter: i64,

    // We can't just use an InternSet on the rusqlite::types::Value instances, because that
    // includes f64, so it's not Hash or Eq.
    // Instead we track byte and String arguments separately, mapping them to their argument name,
    // in order to dedupe. We'll add these to the regular argument vector later.
    byte_args: HashMap<Vec<u8>, String>,             // From value to argument name.
    string_args: HashMap<Rc<String>, String>,        // From value to argument name.
    args: Vec<(String, Rc<rusqlite::types::Value>)>, // (arg, value).
}

impl SQLiteQueryBuilder {
    pub fn new() -> Self {
        SQLiteQueryBuilder::with_prefix("$v".to_string())
    }

    pub fn with_prefix(prefix: String) -> Self {
        SQLiteQueryBuilder {
            sql: String::new(),
            arg_prefix: prefix,
            arg_counter: 0,

            byte_args: HashMap::default(),
            string_args: HashMap::default(),
            args: vec![],
        }
    }

    fn next_argument_name(&mut self) -> String {
        let arg = format!("{}{}", self.arg_prefix, self.arg_counter);
        self.arg_counter = self.arg_counter + 1;
        arg
    }

    fn push_static_arg(&mut self, val: Rc<rusqlite::types::Value>) {
        // TODO: intern these, too.
        let arg = self.next_argument_name();
        self.push_named_arg(arg.as_str());
        self.args.push((arg, val));
    }

    fn push_named_arg(&mut self, arg: &str) {
        self.push_sql(arg);
    }
}

impl QueryBuilder for SQLiteQueryBuilder {
    fn push_sql(&mut self, sql: &str) {
        self.sql.push_str(sql);
    }

    fn push_identifier(&mut self, identifier: &str) -> BuildQueryResult {
        self.push_sql("`");
        self.push_sql(&identifier.replace("`", "``"));
        self.push_sql("`");
        Ok(())
    }

    fn push_typed_value(&mut self, value: &TypedValue) -> BuildQueryResult {
        use TypedValue::*;
        match value {
            &Ref(entid) => self.push_sql(entid.to_string().as_str()),
            &Boolean(v) => self.push_sql(if v { "1" } else { "0" }),
            &Long(v) => self.push_sql(v.to_string().as_str()),
            &Double(OrderedFloat(v)) => self.push_sql(v.to_string().as_str()),
            &Instant(dt) => {
                self.push_sql(format!("{}", dt.to_micros()).as_str());      // TODO: argument instead?
            },
            &Uuid(ref u) => {
                let bytes = u.as_bytes();
                if let Some(arg) = self.byte_args.get(bytes.as_ref()).cloned() {        // Why, borrow checker, why?!
                    self.push_named_arg(arg.as_str());
                } else {
                    let arg = self.next_argument_name();
                    self.push_named_arg(arg.as_str());
                    self.byte_args.insert(bytes.clone().to_vec(), arg);
                }
            },
            // These are both `Rc`. Unfortunately, we can't use that fact when
            // turning these into rusqlite Values.
            // However, we can check to see whether there's an existing var that matches…
            &String(ref s) => {
                if let Some(arg) = self.string_args.get(s).cloned() {
                    self.push_named_arg(arg.as_str());
                } else {
                    let arg = self.next_argument_name();
                    self.push_named_arg(arg.as_str());
                    self.string_args.insert(s.clone(), arg);
                }
            },
            &Keyword(ref s) => {
                // TODO: intern.
                let v = Rc::new(rusqlite::types::Value::Text(s.as_ref().to_string()));
                self.push_static_arg(v);
            },
        }
        Ok(())
    }

    /// Our bind parameters will be interleaved with pushed `TypedValue` instances. That means we
    /// need to use named parameters, not positional parameters.
    /// The `name` argument to this method is expected to be alphanumeric. If not, this method
    /// returns an `InvalidParameterName` error result.
    /// Callers should make sure that the name doesn't overlap with generated parameter names. If
    /// it does, `BindParamCouldBeGenerated` is the error.
    fn push_bind_param(&mut self, name: &str) -> BuildQueryResult {
        // Do some validation first.
        // This is not free, but it's probably worth it for now.
        if !name.chars().all(|c| char::is_alphanumeric(c) || c == '_') {
            bail!(ErrorKind::InvalidParameterName(name.to_string()));
        }

        if name.starts_with(self.arg_prefix.as_str()) &&
           name.chars().skip(self.arg_prefix.len()).all(char::is_numeric) {
               bail!(ErrorKind::BindParamCouldBeGenerated(name.to_string()));
        }

        self.push_sql("$");
        self.push_sql(name);
        Ok(())
    }

    fn finish(self) -> SQLQuery {
        // We collected string and byte arguments into separate maps so that we could
        // dedupe them. Now we need to turn them into rusqlite Values.
        let mut args = self.args;
        let string_args = self.string_args.into_iter().map(|(val, arg)| {
             (arg, Rc::new(rusqlite::types::Value::Text(val.as_ref().clone())))
        });
        let byte_args = self.byte_args.into_iter().map(|(val, arg)| {
            (arg, Rc::new(rusqlite::types::Value::Blob(val)))
        });

        args.extend(string_args);
        args.extend(byte_args);

        // Get the args in the right order -- $v0, $v1…
        args.sort_by(|&(ref k1, _), &(ref k2, _)| k1.cmp(k2));
        SQLQuery {
            sql: self.sql,
            args: args,
        }
    }
}

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

    fn string_arg(s: &str) -> Rc<rusqlite::types::Value> {
        Rc::new(rusqlite::types::Value::Text(s.to_string()))
    }

    #[test]
    fn test_sql() {
        let mut s = SQLiteQueryBuilder::new();
        s.push_sql("SELECT ");
        s.push_identifier("foo").unwrap();
        s.push_sql(" WHERE ");
        s.push_identifier("bar").unwrap();
        s.push_sql(" = ");
        s.push_static_arg(string_arg("frobnicate"));
        s.push_sql(" OR ");
        s.push_static_arg(string_arg("swoogle"));
        let q = s.finish();

        assert_eq!(q.sql.as_str(), "SELECT `foo` WHERE `bar` = $v0 OR $v1");
        assert_eq!(q.args,
                   vec![("$v0".to_string(), string_arg("frobnicate")),
                        ("$v1".to_string(), string_arg("swoogle"))]);
    }
}
<|endoftext|>


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

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

/// A logical scope in the AST. Represents any scoped block of Javascript code.
/// 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 {
    heap: Rc<RefCell<AllocBox>>,
    locals: HashMap<Binding, UniqueBinding>,
    stack: HashMap<UniqueBinding, JsVar>,
    pub tag: ScopeTag,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ScopeTag {
    Call,
    Closure(UniqueBinding),
    Block,
}

#[derive(Copy, Clone, Debug)]
pub enum LookupError {
    Unreachable,
    FnBoundary,
    CheckParent,
}

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

    #[inline]
    pub fn len(&self) -> usize {
        self.stack.len()
    }

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

    /// Push an already-allocated JsVar onto the stack.
    pub fn bind_var(&mut self, var: JsVar) {
        // Create a mapping from the local binding to the unique binding
        self.locals.insert(var.binding.clone(), var.unique.clone());
        // Push the unique binding onto the stack
        self.stack.insert(var.unique.clone(), var);
    }

    fn rebind_var(&mut self, local: Binding, unique: UniqueBinding, var: JsVar) {
        self.locals.insert(local, unique.clone());
        self.stack.insert(unique, var);
    }

    /// Return an optional copy of a variable and an optional pointer into the heap.
    pub fn get_var_copy(&self, local: &Binding) -> result::Result<(JsVar, Option<JsPtrEnum>), LookupError> {
        if let Some(unique) = self.locals.get(local) {
            if let Some(var) = self.stack.get(unique) {
                match var.t {
                    JsType::JsPtr(_) => {
                        if let Some(alloc) = self.heap.borrow().find_id(unique) {
                            Ok((var.clone(), Some(alloc.borrow().clone())))
                        } else {
                            // This case should be impossible unless you have an
                            // invalid ptr, which should also be impossible.
                            Err(LookupError::Unreachable)
                        }
                    },
                    _ => Ok((var.clone(), None)),
                }
            } else { Err(LookupError::Unreachable) }
        } else if self.tag == ScopeTag::Call || matches!(self.tag, ScopeTag::Closure(_)) {
            // A nonexistent binding in the current scope might require searching
            // the scope tree upwards for the binding. However, if the current
            // scope is a function call, it does not have access to anything from
            // its parent scope, so it should not do this lookup. If the overall
            // lookup fails, the ScopeManager will check the global scope.
            Err(LookupError::FnBoundary)
        } else {
            Err(LookupError::CheckParent)
        }
    }

    /// Try to update a variable that's been allocated.
    pub fn update_var(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<()> {
        if !self.locals.contains_key(&var.binding) {
            // Variable was not allocated.
            return Err(GcError::Store(var, ptr));
        }
        match var.t {
            JsType::JsPtr(ref tag) =>
                if let Some(ref ptr) = ptr {
                    // If the pointer and its underlying type are not equal, return an error.
                    if !tag.eq_ptr_type(&ptr) { return Err(GcError::PtrAlloc); }
                    // A new root was potentially created
                    // TODO FIXME? Cloning ptr is potentially expensive
                    self.heap.borrow_mut().update_ptr(&var.unique, ptr.clone())?;
                } else {
                    return Err(GcError::PtrAlloc);
                },
            _ => {
                if let Some(_) = ptr {
                    return Err(GcError::PtrAlloc);
                }
                // A root was potentially removed.
                // Blindly accept this Result, since we have no information
                // about the type we're overwriting, and if we fail to condemn
                // a stack-allocated variable that's completely fine, since the
                // heap doesn't store those anyway.
                self.heap.borrow_mut().condemn(var.unique.clone()).ok();
            },
        }
        // Update the variable on the stack
        if let Entry::Occupied(mut view) = self.stack.entry(var.unique.clone()) {
            *view.get_mut() = var;
            Ok(())
        } else {
            Err(GcError::Store(var, ptr))
        }
    }

    pub fn trigger_gc(&mut self) {
        // The interpreter says we can GC now
        self.heap.borrow_mut().mark_ptrs();
        self.heap.borrow_mut().sweep_ptrs();
        // Pop any heap-allocated variables we just deleted
        let uniques = self.stack.clone();
        for (unique, var) in uniques {
            match var.t {
                JsType::JsPtr(_) =>
                    if self.heap.borrow().find_id(&unique).is_none() {
                        self.stack.remove(&unique);
                    },
                _ => {},
            }
        }
        let locals = self.locals.clone();
        for (local, unique) in locals {
            if !self.stack.contains_key(&unique) {
                self.locals.remove(&local);
            }
        }
    }

    /// 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, parent: &mut Scope, returning_closure: bool) -> Result<()> {
        for (local, unique) in self.locals.drain() {
            let var = match self.stack.remove(&unique) {
                Some(var) => var,
                None => return Err(GcError::Scope),
            };
            if returning_closure {
                // If we're returning a closure, conservatively assume the
                // closure takes ownership of every binding defined in this
                // scope, so it must all live into the parent scope.
                parent.rebind_var(local, unique, var);
            } else {
                // If not returning a closure, rebind all heap-allocated
                // variables into the parent scope, so they may be GC'd at a
                // later time.
                if let JsType::JsPtr(_) = var.t {
                    parent.rebind_var(local, unique, var);
                }
            }
        }
        Ok(())
    }
}

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

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

    #[test]
    fn test_push_var() {
        let heap = test_utils::make_alloc_box();
        let mut test_scope = Scope::new(ScopeTag::Block, &heap);
        let (var, ptr) = test_utils::make_str("test");
        assert!(test_scope.push_var(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(var, None).is_ok());
        assert_eq!(test_scope.heap.borrow().len(), 1);
    }

    #[test]
    fn test_push_var_fail() {
        let heap = test_utils::make_alloc_box();
        let mut test_scope = Scope::new(ScopeTag::Block, &heap);
        let (var, ptr) = test_utils::make_str("test");
        let res = test_scope.push_var(var, None);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::PtrAlloc)));
        assert!(test_scope.heap.borrow().is_empty());
        let var = test_utils::make_num(1.);
        let res = test_scope.push_var(var, Some(ptr));
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::PtrAlloc)));
        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(ScopeTag::Block, &heap);
        let (x, x_ptr) = test_utils::make_str("x");
        let x_bnd = x.binding.clone();
        test_scope.push_var(x, Some(x_ptr)).unwrap();

        let copy = test_scope.get_var_copy(&x_bnd);
        assert!(copy.is_ok());
        let (var_copy, ptr_copy) = copy.unwrap();
        assert!(matches!(var_copy, JsVar { t: JsType::JsPtr(_), .. }));
        assert!(ptr_copy.is_some());
    }

    #[test]
    fn test_get_var_copy_fail() {
        let heap = test_utils::make_alloc_box();
        let test_scope = Scope::new(ScopeTag::Block, &heap);
        let copy = test_scope.get_var_copy(&Binding::new("".to_string()));
        assert!(copy.is_err());
    }

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

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

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

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

    #[test]
    fn test_transfer_stack_no_closure() {
        let heap = test_utils::make_alloc_box();
        let mut parent_scope = Scope::new(ScopeTag::Block, &heap);
        {
            let mut test_scope = Scope::new(ScopeTag::Block, &heap);
            test_scope.push_var(test_utils::make_num(0.), None).unwrap();
            test_scope.push_var(test_utils::make_num(1.), None).unwrap();
            test_scope.push_var(test_utils::make_num(2.), None).unwrap();
            let kvs = vec![(JsKey::JsSym("true".to_string()),
                            test_utils::make_num(1.), None)];
            let (var, ptr) = test_utils::make_obj(kvs, heap.clone());
            test_scope.push_var(var, Some(ptr)).unwrap();
            test_scope.transfer_stack(&mut parent_scope, false).unwrap();
        }
        assert_eq!(parent_scope.stack.len(), 1);
    }

    #[test]
    fn test_transfer_stack_return_closure() {
        let heap = test_utils::make_alloc_box();
        let mut closure_scope = Scope::new(ScopeTag::Block, &heap);
        let fn_unique = {
            // Create a child scope
            let mut test_scope = Scope::new(ScopeTag::Block, &heap);

            // Create a function object
            let (var, test_fn) = test_utils::make_fn(&Some("test".to_owned()), &Vec::new());
            let fn_unique = var.unique.clone();

            // Alocate the function
            test_scope.push_var(var, Some(test_fn)).unwrap();

            // Create and allocate a number
            test_scope.push_var(test_utils::make_num(1.), None).unwrap();

            // Create and allocate a string
            let (var, ptr) = test_utils::make_str("test");
            test_scope.push_var(var, Some(ptr)).unwrap();

            // Kill the current scope, signalling that we're returning a closure
            test_scope.transfer_stack(&mut closure_scope, true).unwrap();
            fn_unique
        };
        // The closure scope should contain the entire environment of the old scope
        assert_eq!(closure_scope.stack.len(), 3);
        // The heap should contain a string and a function
        assert_eq!(heap.borrow().len(), 2);
        // The function should still be allocated
        assert!(heap.borrow().find_id(&fn_unique).is_some());
    }
}
<commit_msg>Mangle local bindings when rebinding them in transfer_stack<commit_after>use std::cell::RefCell;
use std::collections::hash_map::{Entry, HashMap};
use std::rc::Rc;
use std::result;

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

/// A logical scope in the AST. Represents any scoped block of Javascript code.
/// 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 {
    heap: Rc<RefCell<AllocBox>>,
    locals: HashMap<Binding, UniqueBinding>,
    stack: HashMap<UniqueBinding, JsVar>,
    pub tag: ScopeTag,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ScopeTag {
    Call,
    Closure(UniqueBinding),
    Block,
}

#[derive(Copy, Clone, Debug)]
pub enum LookupError {
    Unreachable,
    FnBoundary,
    CheckParent,
}

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

    #[inline]
    pub fn len(&self) -> usize {
        self.stack.len()
    }

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

    /// Push an already-allocated JsVar onto the stack.
    pub fn bind_var(&mut self, var: JsVar) {
        // Create a mapping from the local binding to the unique binding
        self.locals.insert(var.binding.clone(), var.unique.clone());
        // Push the unique binding onto the stack
        self.stack.insert(var.unique.clone(), var);
    }

    fn rebind_var(&mut self, local: Binding, unique: UniqueBinding, var: JsVar) {
        self.locals.insert(local, unique.clone());
        self.stack.insert(unique, var);
    }

    /// Return an optional copy of a variable and an optional pointer into the heap.
    pub fn get_var_copy(&self, local: &Binding) -> result::Result<(JsVar, Option<JsPtrEnum>), LookupError> {
        if let Some(unique) = self.locals.get(local) {
            if let Some(var) = self.stack.get(unique) {
                match var.t {
                    JsType::JsPtr(_) => {
                        if let Some(alloc) = self.heap.borrow().find_id(unique) {
                            Ok((var.clone(), Some(alloc.borrow().clone())))
                        } else {
                            // This case should be impossible unless you have an
                            // invalid ptr, which should also be impossible.
                            Err(LookupError::Unreachable)
                        }
                    },
                    _ => Ok((var.clone(), None)),
                }
            } else { Err(LookupError::Unreachable) }
        } else if self.tag == ScopeTag::Call || matches!(self.tag, ScopeTag::Closure(_)) {
            // A nonexistent binding in the current scope might require searching
            // the scope tree upwards for the binding. However, if the current
            // scope is a function call, it does not have access to anything from
            // its parent scope, so it should not do this lookup. If the overall
            // lookup fails, the ScopeManager will check the global scope.
            Err(LookupError::FnBoundary)
        } else {
            Err(LookupError::CheckParent)
        }
    }

    /// Try to update a variable that's been allocated.
    pub fn update_var(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<()> {
        if !self.locals.contains_key(&var.binding) {
            // Variable was not allocated.
            return Err(GcError::Store(var, ptr));
        }
        match var.t {
            JsType::JsPtr(ref tag) =>
                if let Some(ref ptr) = ptr {
                    // If the pointer and its underlying type are not equal, return an error.
                    if !tag.eq_ptr_type(&ptr) { return Err(GcError::PtrAlloc); }
                    // A new root was potentially created
                    // TODO FIXME? Cloning ptr is potentially expensive
                    self.heap.borrow_mut().update_ptr(&var.unique, ptr.clone())?;
                } else {
                    return Err(GcError::PtrAlloc);
                },
            _ => {
                if let Some(_) = ptr {
                    return Err(GcError::PtrAlloc);
                }
                // A root was potentially removed.
                // Blindly accept this Result, since we have no information
                // about the type we're overwriting, and if we fail to condemn
                // a stack-allocated variable that's completely fine, since the
                // heap doesn't store those anyway.
                self.heap.borrow_mut().condemn(var.unique.clone()).ok();
            },
        }
        // Update the variable on the stack
        if let Entry::Occupied(mut view) = self.stack.entry(var.unique.clone()) {
            *view.get_mut() = var;
            Ok(())
        } else {
            Err(GcError::Store(var, ptr))
        }
    }

    pub fn trigger_gc(&mut self) {
        // The interpreter says we can GC now
        self.heap.borrow_mut().mark_ptrs();
        self.heap.borrow_mut().sweep_ptrs();
        // Pop any heap-allocated variables we just deleted
        let uniques = self.stack.clone();
        for (unique, var) in uniques {
            match var.t {
                JsType::JsPtr(_) =>
                    if self.heap.borrow().find_id(&unique).is_none() {
                        self.stack.remove(&unique);
                    },
                _ => {},
            }
        }
        let locals = self.locals.clone();
        for (local, unique) in locals {
            if !self.stack.contains_key(&unique) {
                self.locals.remove(&local);
            }
        }
    }

    /// 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, parent: &mut Scope, returning_closure: bool) -> Result<()> {
        for (local, unique) in self.locals.drain() {
            let var = match self.stack.remove(&unique) {
                Some(var) => var,
                None => return Err(GcError::Scope),
            };
            if returning_closure {
                // If we're returning a closure, conservatively assume the
                // closure takes ownership of every binding defined in this
                // scope, so it must all live into the parent scope.
                parent.rebind_var(local, unique, var);
            } else {
                // If not returning a closure, rebind all heap-allocated
                // variables into the parent scope, so they may be GC'd at a
                // later time.
                if let JsType::JsPtr(_) = var.t {
                    let mut var = var;
                    let local = Binding::mangle(&local);
                    var.binding = local.clone();
                    parent.rebind_var(local, unique, var);
                }
            }
        }
        Ok(())
    }
}

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

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

    #[test]
    fn test_push_var() {
        let heap = test_utils::make_alloc_box();
        let mut test_scope = Scope::new(ScopeTag::Block, &heap);
        let (var, ptr) = test_utils::make_str("test");
        assert!(test_scope.push_var(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(var, None).is_ok());
        assert_eq!(test_scope.heap.borrow().len(), 1);
    }

    #[test]
    fn test_push_var_fail() {
        let heap = test_utils::make_alloc_box();
        let mut test_scope = Scope::new(ScopeTag::Block, &heap);
        let (var, ptr) = test_utils::make_str("test");
        let res = test_scope.push_var(var, None);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::PtrAlloc)));
        assert!(test_scope.heap.borrow().is_empty());
        let var = test_utils::make_num(1.);
        let res = test_scope.push_var(var, Some(ptr));
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::PtrAlloc)));
        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(ScopeTag::Block, &heap);
        let (x, x_ptr) = test_utils::make_str("x");
        let x_bnd = x.binding.clone();
        test_scope.push_var(x, Some(x_ptr)).unwrap();

        let copy = test_scope.get_var_copy(&x_bnd);
        assert!(copy.is_ok());
        let (var_copy, ptr_copy) = copy.unwrap();
        assert!(matches!(var_copy, JsVar { t: JsType::JsPtr(_), .. }));
        assert!(ptr_copy.is_some());
    }

    #[test]
    fn test_get_var_copy_fail() {
        let heap = test_utils::make_alloc_box();
        let test_scope = Scope::new(ScopeTag::Block, &heap);
        let copy = test_scope.get_var_copy(&Binding::new("".to_string()));
        assert!(copy.is_err());
    }

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

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

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

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

    #[test]
    fn test_transfer_stack_no_closure() {
        let heap = test_utils::make_alloc_box();
        let mut parent_scope = Scope::new(ScopeTag::Block, &heap);
        {
            let mut test_scope = Scope::new(ScopeTag::Block, &heap);
            test_scope.push_var(test_utils::make_num(0.), None).unwrap();
            test_scope.push_var(test_utils::make_num(1.), None).unwrap();
            test_scope.push_var(test_utils::make_num(2.), None).unwrap();
            let kvs = vec![(JsKey::JsSym("true".to_string()),
                            test_utils::make_num(1.), None)];
            let (var, ptr) = test_utils::make_obj(kvs, heap.clone());
            test_scope.push_var(var, Some(ptr)).unwrap();
            test_scope.transfer_stack(&mut parent_scope, false).unwrap();
        }
        assert_eq!(parent_scope.stack.len(), 1);
    }

    #[test]
    fn test_transfer_stack_return_closure() {
        let heap = test_utils::make_alloc_box();
        let mut closure_scope = Scope::new(ScopeTag::Block, &heap);
        let fn_unique = {
            // Create a child scope
            let mut test_scope = Scope::new(ScopeTag::Block, &heap);

            // Create a function object
            let (var, test_fn) = test_utils::make_fn(&Some("test".to_owned()), &Vec::new());
            let fn_unique = var.unique.clone();

            // Alocate the function
            test_scope.push_var(var, Some(test_fn)).unwrap();

            // Create and allocate a number
            test_scope.push_var(test_utils::make_num(1.), None).unwrap();

            // Create and allocate a string
            let (var, ptr) = test_utils::make_str("test");
            test_scope.push_var(var, Some(ptr)).unwrap();

            // Kill the current scope, signalling that we're returning a closure
            test_scope.transfer_stack(&mut closure_scope, true).unwrap();
            fn_unique
        };
        // The closure scope should contain the entire environment of the old scope
        assert_eq!(closure_scope.stack.len(), 3);
        // The heap should contain a string and a function
        assert_eq!(heap.borrow().len(), 2);
        // The function should still be allocated
        assert!(heap.borrow().find_id(&fn_unique).is_some());
    }
}
<|endoftext|>


<commit_before>//! Codegen `extern "platform-intrinsic"` intrinsics.

use super::*;
use crate::prelude::*;

pub(super) fn codegen_simd_intrinsic_call<'tcx>(
    fx: &mut FunctionCx<'_, '_, 'tcx>,
    instance: Instance<'tcx>,
    args: &[mir::Operand<'tcx>],
    ret: CPlace<'tcx>,
    span: Span,
) {
    let def_id = instance.def_id();
    let substs = instance.substs;

    let intrinsic = fx.tcx.item_name(def_id).as_str();
    let intrinsic = &intrinsic[..];

    intrinsic_match! {
        fx, intrinsic, substs, args,
        _ => {
            fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic));
        };

        simd_cast, (c a) {
            validate_simd_type!(fx, intrinsic, span, a.layout().ty);
            simd_for_each_lane(fx, a, ret, |fx, lane_layout, ret_lane_layout, lane| {
                let ret_lane_ty = fx.clif_type(ret_lane_layout.ty).unwrap();

                let from_signed = type_sign(lane_layout.ty);
                let to_signed = type_sign(ret_lane_layout.ty);

                let ret_lane = clif_int_or_float_cast(fx, lane, from_signed, ret_lane_ty, to_signed);
                CValue::by_val(ret_lane, ret_lane_layout)
            });
        };

        simd_eq, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, Equal|Equal(x, y) -> ret);
        };
        simd_ne, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, NotEqual|NotEqual(x, y) -> ret);
        };
        simd_lt, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, UnsignedLessThan|SignedLessThan|LessThan(x, y) -> ret);
        };
        simd_le, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, UnsignedLessThanOrEqual|SignedLessThanOrEqual|LessThanOrEqual(x, y) -> ret);
        };
        simd_gt, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, UnsignedGreaterThan|SignedGreaterThan|GreaterThan(x, y) -> ret);
        };
        simd_ge, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(
                fx,
                UnsignedGreaterThanOrEqual|SignedGreaterThanOrEqual|GreaterThanOrEqual
                (x, y) -> ret
            );
        };

        // simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U
        _ if intrinsic.starts_with("simd_shuffle"), (c x, c y, o idx) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);

            let n: u16 = intrinsic["simd_shuffle".len()..].parse().unwrap();

            assert_eq!(x.layout(), y.layout());
            let layout = x.layout();

            let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
            let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);

            assert_eq!(lane_ty, ret_lane_ty);
            assert_eq!(u64::from(n), ret_lane_count);

            let total_len = lane_count * 2;

            let indexes = {
                use rustc_middle::mir::interpret::*;
                let idx_const = crate::constant::mir_operand_get_const_val(fx, idx).expect("simd_shuffle* idx not const");

                let idx_bytes = match idx_const {
                    ConstValue::ByRef { alloc, offset } => {
                        let ptr = Pointer::new(AllocId(0 /* dummy */), offset);
                        let size = Size::from_bytes(4 * ret_lane_count /* size_of([u32; ret_lane_count]) */);
                        alloc.get_bytes(fx, ptr, size).unwrap()
                    }
                    _ => unreachable!("{:?}", idx_const),
                };

                (0..ret_lane_count).map(|i| {
                    let i = usize::try_from(i).unwrap();
                    let idx = rustc_middle::mir::interpret::read_target_uint(
                        fx.tcx.data_layout.endian,
                        &idx_bytes[4*i.. 4*i + 4],
                    ).expect("read_target_uint");
                    u16::try_from(idx).expect("try_from u32")
                }).collect::<Vec<u16>>()
            };

            for &idx in &indexes {
                assert!(u64::from(idx) < total_len, "idx {} out of range 0..{}", idx, total_len);
            }

            for (out_idx, in_idx) in indexes.into_iter().enumerate() {
                let in_lane = if u64::from(in_idx) < lane_count {
                    x.value_field(fx, mir::Field::new(in_idx.into()))
                } else {
                    y.value_field(fx, mir::Field::new(usize::from(in_idx) - usize::try_from(lane_count).unwrap()))
                };
                let out_lane = ret.place_field(fx, mir::Field::new(out_idx));
                out_lane.write_cvalue(fx, in_lane);
            }
        };

        simd_insert, (c base, o idx, c val) {
            // FIXME validate
            let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
                idx_const
            } else {
                fx.tcx.sess.span_fatal(
                    span,
                    "Index argument for `simd_insert` is not a constant",
                );
            };

            let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
            let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx);
            if idx >= lane_count.into() {
                fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count));
            }

            ret.write_cvalue(fx, base);
            let ret_lane = ret.place_field(fx, mir::Field::new(idx.try_into().unwrap()));
            ret_lane.write_cvalue(fx, val);
        };

        simd_extract, (c v, o idx) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
                idx_const
            } else {
                fx.tcx.sess.span_warn(
                    span,
                    "Index argument for `simd_extract` is not a constant",
                );
                let res = crate::trap::trap_unimplemented_ret_value(
                    fx,
                    ret.layout(),
                    "Index argument for `simd_extract` is not a constant",
                );
                ret.write_cvalue(fx, res);
                return;
            };

            let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
            let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx);
            if idx >= lane_count.into() {
                fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count));
            }

            let ret_lane = v.value_field(fx, mir::Field::new(idx.try_into().unwrap()));
            ret.write_cvalue(fx, ret_lane);
        };

        simd_add, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_flt_binop!(fx, iadd|fadd(x, y) -> ret);
        };
        simd_sub, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_flt_binop!(fx, isub|fsub(x, y) -> ret);
        };
        simd_mul, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_flt_binop!(fx, imul|fmul(x, y) -> ret);
        };
        simd_div, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_flt_binop!(fx, udiv|sdiv|fdiv(x, y) -> ret);
        };
        simd_shl, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, ishl(x, y) -> ret);
        };
        simd_shr, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, ushr|sshr(x, y) -> ret);
        };
        simd_and, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, band(x, y) -> ret);
        };
        simd_or, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, bor(x, y) -> ret);
        };
        simd_xor, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, bxor(x, y) -> ret);
        };

        simd_fma, (c a, c b, c c) {
            validate_simd_type!(fx, intrinsic, span, a.layout().ty);
            assert_eq!(a.layout(), b.layout());
            assert_eq!(a.layout(), c.layout());
            let layout = a.layout();

            let (lane_count, _lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
            let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
            assert_eq!(lane_count, ret_lane_count);
            let ret_lane_layout = fx.layout_of(ret_lane_ty);

            for lane in 0..lane_count {
                let lane = mir::Field::new(lane.try_into().unwrap());
                let a_lane = a.value_field(fx, lane).load_scalar(fx);
                let b_lane = b.value_field(fx, lane).load_scalar(fx);
                let c_lane = c.value_field(fx, lane).load_scalar(fx);

                let mul_lane = fx.bcx.ins().fmul(a_lane, b_lane);
                let res_lane = CValue::by_val(fx.bcx.ins().fadd(mul_lane, c_lane), ret_lane_layout);

                ret.place_field(fx, lane).write_cvalue(fx, res_lane);
            }
        };

        simd_fmin, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_flt_binop!(fx, fmin(x, y) -> ret);
        };
        simd_fmax, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_flt_binop!(fx, fmax(x, y) -> ret);
        };

        simd_reduce_add_ordered | simd_reduce_add_unordered, (c v) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            simd_reduce(fx, v, ret, |fx, lane_layout, a, b| {
                if lane_layout.ty.is_floating_point() {
                    fx.bcx.ins().fadd(a, b)
                } else {
                    fx.bcx.ins().iadd(a, b)
                }
            });
        };

        simd_reduce_mul_ordered | simd_reduce_mul_unordered, (c v) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            simd_reduce(fx, v, ret, |fx, lane_layout, a, b| {
                if lane_layout.ty.is_floating_point() {
                    fx.bcx.ins().fmul(a, b)
                } else {
                    fx.bcx.ins().imul(a, b)
                }
            });
        };

        simd_reduce_all, (c v) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            simd_reduce_bool(fx, v, ret, |fx, a, b| fx.bcx.ins().band(a, b));
        };

        simd_reduce_any, (c v) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            simd_reduce_bool(fx, v, ret, |fx, a, b| fx.bcx.ins().bor(a, b));
        };

        // simd_fabs
        // simd_saturating_add
        // simd_bitmask
        // simd_select
        // simd_rem
        // simd_neg
    }
}
<commit_msg>Add more SIMD math.h intrinsics<commit_after>//! Codegen `extern "platform-intrinsic"` intrinsics.

use super::*;
use crate::prelude::*;

pub(super) fn codegen_simd_intrinsic_call<'tcx>(
    fx: &mut FunctionCx<'_, '_, 'tcx>,
    instance: Instance<'tcx>,
    args: &[mir::Operand<'tcx>],
    ret: CPlace<'tcx>,
    span: Span,
) {
    let def_id = instance.def_id();
    let substs = instance.substs;

    let intrinsic = fx.tcx.item_name(def_id).as_str();
    let intrinsic = &intrinsic[..];

    intrinsic_match! {
        fx, intrinsic, substs, args,
        _ => {
            fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic));
        };

        simd_cast, (c a) {
            validate_simd_type!(fx, intrinsic, span, a.layout().ty);
            simd_for_each_lane(fx, a, ret, |fx, lane_layout, ret_lane_layout, lane| {
                let ret_lane_ty = fx.clif_type(ret_lane_layout.ty).unwrap();

                let from_signed = type_sign(lane_layout.ty);
                let to_signed = type_sign(ret_lane_layout.ty);

                let ret_lane = clif_int_or_float_cast(fx, lane, from_signed, ret_lane_ty, to_signed);
                CValue::by_val(ret_lane, ret_lane_layout)
            });
        };

        simd_eq, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, Equal|Equal(x, y) -> ret);
        };
        simd_ne, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, NotEqual|NotEqual(x, y) -> ret);
        };
        simd_lt, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, UnsignedLessThan|SignedLessThan|LessThan(x, y) -> ret);
        };
        simd_le, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, UnsignedLessThanOrEqual|SignedLessThanOrEqual|LessThanOrEqual(x, y) -> ret);
        };
        simd_gt, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, UnsignedGreaterThan|SignedGreaterThan|GreaterThan(x, y) -> ret);
        };
        simd_ge, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(
                fx,
                UnsignedGreaterThanOrEqual|SignedGreaterThanOrEqual|GreaterThanOrEqual
                (x, y) -> ret
            );
        };

        // simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U
        _ if intrinsic.starts_with("simd_shuffle"), (c x, c y, o idx) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);

            let n: u16 = intrinsic["simd_shuffle".len()..].parse().unwrap();

            assert_eq!(x.layout(), y.layout());
            let layout = x.layout();

            let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
            let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);

            assert_eq!(lane_ty, ret_lane_ty);
            assert_eq!(u64::from(n), ret_lane_count);

            let total_len = lane_count * 2;

            let indexes = {
                use rustc_middle::mir::interpret::*;
                let idx_const = crate::constant::mir_operand_get_const_val(fx, idx).expect("simd_shuffle* idx not const");

                let idx_bytes = match idx_const {
                    ConstValue::ByRef { alloc, offset } => {
                        let ptr = Pointer::new(AllocId(0 /* dummy */), offset);
                        let size = Size::from_bytes(4 * ret_lane_count /* size_of([u32; ret_lane_count]) */);
                        alloc.get_bytes(fx, ptr, size).unwrap()
                    }
                    _ => unreachable!("{:?}", idx_const),
                };

                (0..ret_lane_count).map(|i| {
                    let i = usize::try_from(i).unwrap();
                    let idx = rustc_middle::mir::interpret::read_target_uint(
                        fx.tcx.data_layout.endian,
                        &idx_bytes[4*i.. 4*i + 4],
                    ).expect("read_target_uint");
                    u16::try_from(idx).expect("try_from u32")
                }).collect::<Vec<u16>>()
            };

            for &idx in &indexes {
                assert!(u64::from(idx) < total_len, "idx {} out of range 0..{}", idx, total_len);
            }

            for (out_idx, in_idx) in indexes.into_iter().enumerate() {
                let in_lane = if u64::from(in_idx) < lane_count {
                    x.value_field(fx, mir::Field::new(in_idx.into()))
                } else {
                    y.value_field(fx, mir::Field::new(usize::from(in_idx) - usize::try_from(lane_count).unwrap()))
                };
                let out_lane = ret.place_field(fx, mir::Field::new(out_idx));
                out_lane.write_cvalue(fx, in_lane);
            }
        };

        simd_insert, (c base, o idx, c val) {
            // FIXME validate
            let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
                idx_const
            } else {
                fx.tcx.sess.span_fatal(
                    span,
                    "Index argument for `simd_insert` is not a constant",
                );
            };

            let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
            let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx);
            if idx >= lane_count.into() {
                fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count));
            }

            ret.write_cvalue(fx, base);
            let ret_lane = ret.place_field(fx, mir::Field::new(idx.try_into().unwrap()));
            ret_lane.write_cvalue(fx, val);
        };

        simd_extract, (c v, o idx) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
                idx_const
            } else {
                fx.tcx.sess.span_warn(
                    span,
                    "Index argument for `simd_extract` is not a constant",
                );
                let res = crate::trap::trap_unimplemented_ret_value(
                    fx,
                    ret.layout(),
                    "Index argument for `simd_extract` is not a constant",
                );
                ret.write_cvalue(fx, res);
                return;
            };

            let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
            let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx);
            if idx >= lane_count.into() {
                fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count));
            }

            let ret_lane = v.value_field(fx, mir::Field::new(idx.try_into().unwrap()));
            ret.write_cvalue(fx, ret_lane);
        };

        simd_add, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_flt_binop!(fx, iadd|fadd(x, y) -> ret);
        };
        simd_sub, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_flt_binop!(fx, isub|fsub(x, y) -> ret);
        };
        simd_mul, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_flt_binop!(fx, imul|fmul(x, y) -> ret);
        };
        simd_div, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_flt_binop!(fx, udiv|sdiv|fdiv(x, y) -> ret);
        };
        simd_shl, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, ishl(x, y) -> ret);
        };
        simd_shr, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, ushr|sshr(x, y) -> ret);
        };
        simd_and, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, band(x, y) -> ret);
        };
        simd_or, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, bor(x, y) -> ret);
        };
        simd_xor, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, bxor(x, y) -> ret);
        };

        simd_fma, (c a, c b, c c) {
            validate_simd_type!(fx, intrinsic, span, a.layout().ty);
            assert_eq!(a.layout(), b.layout());
            assert_eq!(a.layout(), c.layout());
            let layout = a.layout();

            let (lane_count, _lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
            let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
            assert_eq!(lane_count, ret_lane_count);
            let ret_lane_layout = fx.layout_of(ret_lane_ty);

            for lane in 0..lane_count {
                let lane = mir::Field::new(lane.try_into().unwrap());
                let a_lane = a.value_field(fx, lane).load_scalar(fx);
                let b_lane = b.value_field(fx, lane).load_scalar(fx);
                let c_lane = c.value_field(fx, lane).load_scalar(fx);

                let mul_lane = fx.bcx.ins().fmul(a_lane, b_lane);
                let res_lane = CValue::by_val(fx.bcx.ins().fadd(mul_lane, c_lane), ret_lane_layout);

                ret.place_field(fx, lane).write_cvalue(fx, res_lane);
            }
        };

        simd_fmin, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_flt_binop!(fx, fmin(x, y) -> ret);
        };
        simd_fmax, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_flt_binop!(fx, fmax(x, y) -> ret);
        };

        simd_reduce_add_ordered | simd_reduce_add_unordered, (c v) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            simd_reduce(fx, v, ret, |fx, lane_layout, a, b| {
                if lane_layout.ty.is_floating_point() {
                    fx.bcx.ins().fadd(a, b)
                } else {
                    fx.bcx.ins().iadd(a, b)
                }
            });
        };

        simd_reduce_mul_ordered | simd_reduce_mul_unordered, (c v) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            simd_reduce(fx, v, ret, |fx, lane_layout, a, b| {
                if lane_layout.ty.is_floating_point() {
                    fx.bcx.ins().fmul(a, b)
                } else {
                    fx.bcx.ins().imul(a, b)
                }
            });
        };

        simd_reduce_all, (c v) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            simd_reduce_bool(fx, v, ret, |fx, a, b| fx.bcx.ins().band(a, b));
        };

        simd_reduce_any, (c v) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            simd_reduce_bool(fx, v, ret, |fx, a, b| fx.bcx.ins().bor(a, b));
        };

        // simd_fabs
        // simd_saturating_add
        // simd_bitmask
        // simd_select
        // simd_rem
        // simd_neg
        // simd_trunc
        // simd_floor
    }
}
<|endoftext|>


<commit_before>use multi_objective::MultiObjective;
use non_dominated_sort::{NonDominatedSort, SolutionWithIndex};
use crowding_distance::{assign_crowding_distance, AssignedCrowdingDistance};
use std::cmp::PartialOrd;

/// Select `n` solutions using the approach taken by NSGA. We first sort
/// the solutions into their corresponding pareto fronts. Then, we put
/// as many "whole" fronts into the result as possible, until we cannot
/// fit a whole front into the result (otherwise we would have more than
/// `n` solutions). For this last front, sort it's solutions according
/// to their crowding distance (higher crowding distance is better!),
/// and chose those solutions with the higher crowding distance.

pub fn selection_nsga<'a, S>(
    solutions: &'a [S],
    n: usize,
    multi_objective: &MultiObjective<S, f64>,
) -> Vec<AssignedCrowdingDistance<'a, S>>
where
    S: 'a,
{
    // Cannot select more solutions than we actually have
    let n = solutions.len().min(n);
    debug_assert!(n <= solutions.len());
    let mut result = Vec::with_capacity(n);

    for front in NonDominatedSort::new(solutions, multi_objective) {
        if result.len() + front.solutions.len() <= n {
            // the whole front fits into the result

            // NOTE: I think in the original paper, the authors would
            // not defined the crowding distance. But in order to select
            // parents to reproduce, having the crowding distance
            // readily available is desired.
            let (mut assigned_crowding, _) = assign_crowding_distance(&front, multi_objective);

            for s in assigned_crowding {
                result.push(s);
                debug_assert!(result.len() <= n);
            }
        } else {
            // the front does not fit in total.
            // we will sort it's solutions according to the crowding distance and take the best
            // until we have "n" solutions in the result

            let (mut assigned_crowding, _) = assign_crowding_distance(&front, multi_objective);

            assigned_crowding.sort_by(|a, b| {
                debug_assert_eq!(a.rank, b.rank);
                a.crowding_distance
                    .partial_cmp(&b.crowding_distance)
                    .unwrap()
                    .reverse()
            });

            debug_assert!(n >= result.len());

            for s in assigned_crowding.into_iter().take(n - result.len()) {
                result.push(s);
                debug_assert!(result.len() <= n);
            }

            debug_assert_eq!(n, result.len());
            break;
        }

        if result.len() >= n {
            break;
        }
    }

    debug_assert_eq!(n, result.len());

    return result;
}
<commit_msg>Refactor selection_nsga()<commit_after>use multi_objective::MultiObjective;
use non_dominated_sort::NonDominatedSort;
use crowding_distance::{assign_crowding_distance, AssignedCrowdingDistance};
use std::cmp::PartialOrd;

/// Select `n` solutions using the approach taken by NSGA. We first sort
/// the solutions into their corresponding pareto fronts. Then, we put
/// as many "whole" fronts into the result as possible, until we cannot
/// fit a whole front into the result (otherwise we would have more than
/// `n` solutions). For this last front, sort it's solutions according
/// to their crowding distance (higher crowding distance is better!),
/// and chose those solutions with the higher crowding distance.

pub fn selection_nsga<'a, S>(
    solutions: &'a [S],
    n: usize,
    multi_objective: &MultiObjective<S, f64>,
) -> Vec<AssignedCrowdingDistance<'a, S>>
where
    S: 'a,
{
    // Cannot select more solutions than we actually have
    let n = solutions.len().min(n);
    debug_assert!(n <= solutions.len());

    let mut result = Vec::with_capacity(n);
    let mut missing_solutions = n;

    for front in NonDominatedSort::new(solutions, multi_objective) {
        let (mut assigned_crowding, _) = assign_crowding_distance(&front, multi_objective);

        if assigned_crowding.len() <= missing_solutions {
            // the whole front fits into the result
            missing_solutions -= assigned_crowding.len();
            result.extend(assigned_crowding);

            if missing_solutions == 0 {
                break;
            }
        } else {
            // the front does not fit in total. sort it's solutions
            // according to the crowding distance and take the best
            // until we have "n" solutions in the result.

            assigned_crowding.sort_by(|a, b| {
                debug_assert_eq!(a.rank, b.rank);
                a.crowding_distance
                    .partial_cmp(&b.crowding_distance)
                    .unwrap()
                    .reverse()
            });

            result.extend(assigned_crowding.into_iter().take(missing_solutions));

            break;
        }
    }

    debug_assert_eq!(n, result.len());

    return result;
}
<|endoftext|>


<commit_before>use prelude::*;

cfg_if! {
    if #[cfg(feature = "std")] {
        use std::fmt;
        use std::str;
    } else {
        use core::fmt;
        use core::str;
    }
}

impl fmt::Display for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::LowerHex::fmt(self, f)
    }
}

impl fmt::Display for UuidVariant {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            UuidVariant::NCS => write!(f, "NCS"),
            UuidVariant::RFC4122 => write!(f, "RFC4122"),
            UuidVariant::Microsoft => write!(f, "Microsoft"),
            UuidVariant::Future => write!(f, "Future"),
        }
    }
}

impl fmt::LowerHex for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <super::Hyphenated as fmt::LowerHex>::fmt(&self.hyphenated(), f)
    }
}

impl fmt::UpperHex for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <super::Hyphenated as fmt::UpperHex>::fmt(&self.hyphenated(), f)
    }
}

impl str::FromStr for Uuid {
    type Err = super::ParseError;

    fn from_str(uuid_str: &str) -> Result<Uuid, super::ParseError> {
        Uuid::parse_str(uuid_str)
    }
}

impl Default for Uuid {
    fn default() -> Self {
        Uuid::nil()
    }
}

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

    macro_rules! check {
        ($buf:ident, $format:expr, $target:expr, $len:expr, $cond:expr) => {
            $buf.clear();
            write!($buf, $format, $target).unwrap();
            assert!($buf.len() == $len);
            assert!($buf.chars().all($cond), "{}", $buf);
        };
    }

    #[test]
    fn test_uuid_compare() {
        let uuid1 = test_util::new();
        let uuid2 = test_util::new2();

        assert_eq!(uuid1, uuid1);
        assert_eq!(uuid2, uuid2);

        assert_ne!(uuid1, uuid2);
        assert_ne!(uuid2, uuid1);
    }

    #[test]
    fn test_uuid_default() {
        let default_uuid = Uuid::default();
        let nil_uuid = Uuid::nil();

        assert_eq!(default_uuid, nil_uuid);
    }

    #[test]
    fn test_uuid_display() {
        use std::fmt::Write;

        let uuid = test_util::new();
        let s = uuid.to_string();
        let mut buffer = String::new();

        assert_eq!(s, uuid.hyphenated().to_string());

        check!(buffer, "{}", uuid, 36, |c| c.is_lowercase() || c.is_digit(10)
            || c == '-');
    }

    #[test]
    fn test_uuid_lowerhex() {
        use std::fmt::Write;

        let mut buffer = String::new();
        let uuid = test_util::new();

        check!(buffer, "{:x}", uuid, 36, |c| c.is_lowercase() || c.is_digit(10)
            || c == '-');
    }

    #[test]
    fn test_uuid_operator_eq() {
        let uuid1 = test_util::new();
        let uuid1_dup = uuid1.clone();
        let uuid2 = test_util::new2();

        assert!(uuid1 == uuid1);
        assert!(uuid1 == uuid1_dup);
        assert!(uuid1_dup == uuid1);

        assert!(uuid1 != uuid2);
        assert!(uuid2 != uuid1);
        assert!(uuid1_dup != uuid2);
        assert!(uuid2 != uuid1_dup);
    }

    #[test]
    fn test_uuid_to_string() {
        use std::fmt::Write;

        let uuid = test_util::new();
        let s = uuid.to_string();
        let mut buffer = String::new();

        assert_eq!(s.len(), 36);
        check!(buffer, "{}", s, 36, |c| c.is_lowercase() || c.is_digit(10)
            || c == '-');
    }

    #[test]
    fn test_uuid_upperhex() {
        use std::fmt::Write;

        let mut buffer = String::new();
        let uuid = test_util::new();

        check!(buffer, "{:X}", uuid, 36, |c| c.is_uppercase() || c.is_digit(10)
            || c == '-');
    }
}
<commit_msg>fix format and imports<commit_after>use prelude::*;

cfg_if! {
    if #[cfg(feature = "std")] {
        use std::fmt;
        use std::str;
    } else {
        use core::fmt;
        use core::str;
    }
}

impl fmt::Display for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::LowerHex::fmt(self, f)
    }
}

impl fmt::Display for UuidVariant {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            UuidVariant::NCS => write!(f, "NCS"),
            UuidVariant::RFC4122 => write!(f, "RFC4122"),
            UuidVariant::Microsoft => write!(f, "Microsoft"),
            UuidVariant::Future => write!(f, "Future"),
        }
    }
}

impl fmt::LowerHex for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <super::Hyphenated as fmt::LowerHex>::fmt(&self.hyphenated(), f)
    }
}

impl fmt::UpperHex for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <super::Hyphenated as fmt::UpperHex>::fmt(&self.hyphenated(), f)
    }
}

impl str::FromStr for Uuid {
    type Err = super::ParseError;

    fn from_str(uuid_str: &str) -> Result<Uuid, super::ParseError> {
        Uuid::parse_str(uuid_str)
    }
}

impl Default for Uuid {
    fn default() -> Self {
        Uuid::nil()
    }
}

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

    macro_rules! check {
        ($buf:ident, $format:expr, $target:expr, $len:expr, $cond:expr) => {
            $buf.clear();
            write!($buf, $format, $target).unwrap();
            assert!($buf.len() == $len);
            assert!($buf.chars().all($cond), "{}", $buf);
        };
    }

    #[test]
    fn test_uuid_compare() {
        let uuid1 = test_util::new();
        let uuid2 = test_util::new2();

        assert_eq!(uuid1, uuid1);
        assert_eq!(uuid2, uuid2);

        assert_ne!(uuid1, uuid2);
        assert_ne!(uuid2, uuid1);
    }

    #[test]
    fn test_uuid_default() {
        let default_uuid = Uuid::default();
        let nil_uuid = Uuid::nil();

        assert_eq!(default_uuid, nil_uuid);
    }

    #[test]
    fn test_uuid_display() {
        use super::fmt::Write;

        let uuid = test_util::new();
        let s = uuid.to_string();
        let mut buffer = String::new();

        assert_eq!(s, uuid.hyphenated().to_string());

        check!(
            buffer,
            "{}",
            uuid,
            36,
            |c| c.is_lowercase() || c.is_digit(10) || c == '-'
        );
    }

    #[test]
    fn test_uuid_lowerhex() {
        use super::fmt::Write;

        let mut buffer = String::new();
        let uuid = test_util::new();

        check!(
            buffer,
            "{:x}",
            uuid,
            36,
            |c| c.is_lowercase() || c.is_digit(10) || c == '-'
        );
    }

    #[test]
    fn test_uuid_operator_eq() {
        let uuid1 = test_util::new();
        let uuid1_dup = uuid1.clone();
        let uuid2 = test_util::new2();

        assert!(uuid1 == uuid1);
        assert!(uuid1 == uuid1_dup);
        assert!(uuid1_dup == uuid1);

        assert!(uuid1 != uuid2);
        assert!(uuid2 != uuid1);
        assert!(uuid1_dup != uuid2);
        assert!(uuid2 != uuid1_dup);
    }

    #[test]
    fn test_uuid_to_string() {
        use super::fmt::Write;

        let uuid = test_util::new();
        let s = uuid.to_string();
        let mut buffer = String::new();

        assert_eq!(s.len(), 36);

        check!(buffer, "{}", s, 36, |c| c.is_lowercase() || c.is_digit(10)
            || c == '-');
    }

    #[test]
    fn test_uuid_upperhex() {
        use super::fmt::Write;

        let mut buffer = String::new();
        let uuid = test_util::new();

        check!(
            buffer,
            "{:X}",
            uuid,
            36,
            |c| c.is_uppercase() || c.is_digit(10) || c == '-'
        );
    }
}
<|endoftext|>


<commit_before>use std::fmt::Debug;

use rayon::prelude::*;
use rayon;
use anymap;

#[derive(Clone, Default, Debug)]
pub struct Component {
    data: i32,
}

// #[derive(Clone, Default)]
// pub struct VecResource<T> {
//     data: Vec<T>,
// }

#[derive(Clone, Default, Debug)]
pub struct VecResource<T>
    where T: Debug
{
    data: Vec<T>,
}

impl<T> VecResource<T>
    where T: Debug
{
    pub fn new() -> Self {
        VecResource { data: Vec::new() }
    }
}

pub struct Entity((u32, u32));

pub struct World {
    entities: Vec<Entity>,
    components: anymap::AnyMap,
}

impl World {
    pub fn new() -> Self {
        World {
            entities: Vec::new(),
            components: anymap::AnyMap::new(),
        }
    }

    fn register<T>(&mut self)
        where T: Default + 'static
    {
        self.components.insert(T::default());
    }

    fn run<F, T, Eoutput, Einput>(&mut self, inputs: Vec<Einput>, f: F) -> Vec<Eoutput>
        where F: Fn((&T, &Einput)) -> Eoutput + Sync,
              T: Debug + Send + Sync + 'static,
              Eoutput: Send + Sync,
              Einput: Send + Sync
    {
        if let Some(resource) = self.components.get::<VecResource<T>>() {
            let d: &[T] = &resource.data;
            let mut v = Vec::new();
            d.par_iter().zip(&inputs).map(f).collect_into(&mut v);
            v
        } else {
            panic!("Tried to run a system on an unknown component type");
        }
    }

    fn create_entity(&mut self) {
        self.entities.push(Entity((0, 0)));
        let vr: &mut VecResource<u32> = self.components.entry().or_insert_with(VecResource::new);
        vr.data.push(self.entities.len() as u32);
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_world_thingy() {
        let entity_count = 10;
        let mut w = World::new();
        for i in 0..entity_count {
            w.create_entity();
        }
        println!("Bar");
        let mut inputs = Vec::new();
        inputs.resize(entity_count, 1u32);
        // Event input just being a vector is tricksy because it may not
        // be the same length as the entities vector.  It needs to be a particular
        // per-entity mapping of one kind or another.
        // Making it efficient so we don't need to always re-allocate a new vector
        // for returning new events will be a little wacky too.
        let results = w.run(inputs, |(x1, x2): (&u32, &u32)| {
            println!("Xs are is {:?} {:?}", x1, x2);
            x1 + x2
        });
        println!("Results are {:?}", results);
        let mut desired_results: Vec<u32> = Vec::new();
        desired_results.extend(&[2, 3, 4, 5, 6, 7, 8, 9, 10, 11][..]);
        assert_eq!(results, desired_results);
    }
}
<commit_msg>Noodling about with events.  Not sure it's noodling in the right direction.<commit_after>use std::fmt::Debug;
use std::sync::mpsc;

use rayon::prelude::*;
use rayon;
use anymap;

#[derive(Clone, Default, Debug)]
pub struct Component {
    data: i32,
}

// #[derive(Clone, Default)]
// pub struct VecResource<T> {
//     data: Vec<T>,
// }

#[derive(Clone, Default, Debug)]
pub struct VecResource<T>
    where T: Debug
{
    data: Vec<T>,
}

impl<T> VecResource<T>
    where T: Debug
{
    pub fn new() -> Self {
        VecResource { data: Vec::new() }
    }
}

pub struct Entity(u32);

// I feel a little ghetto making one global event type for everything,
// but we'll roll with it for now.
pub struct World<E> {
    entities: Vec<Entity>,
    components: anymap::AnyMap,
    inputs: Vec<InputChannel<E>>,
    outputs: Vec<OutputChannel<E>>
}

pub type InputChannel<T> = mpsc::Receiver<T>;
pub type OutputChannel<T> = mpsc::Sender<T>;

impl<E> World<E> where E: Send + Sync {
    pub fn new() -> Self {
        World {
            entities: Vec::new(),
            components: anymap::AnyMap::new(),
            inputs: Vec::new(),
            outputs: Vec::new(),
        }
    }

    fn register<T>(&mut self)
        where T: Default + 'static
    {
        self.components.insert(T::default());
    }

    fn run<F, T>(&mut self, inputs: Vec<E>, f: F) -> Vec<E>
        where F: Fn((&T, &E)) -> E + Sync,
              T: Debug + Send + Sync + 'static
    {
        if let Some(resource) = self.components.get::<VecResource<T>>() {
            let d: &[T] = &resource.data;
            let mut v = Vec::new();
            d.par_iter().zip(&inputs).map(f).collect_into(&mut v);
            v
        } else {
            panic!("Tried to run a system on an unknown component type");
        }
    }

    fn next_entity(&self) -> Entity {
        use std::u32;
        assert!(self.entities.len() < u32::MAX as usize);
        Entity(self.entities.len() as u32)
    }

    fn create_entity(&mut self) {
        let e = self.next_entity();
        self.entities.push(e);
        let vr: &mut VecResource<u32> = self.components.entry().or_insert_with(VecResource::new);
        vr.data.push(self.entities.len() as u32);

        let (tx, rx) = mpsc::channel();
        self.inputs.push(rx);
        self.outputs.push(tx);
    }

    // fn send_to_entity(&self, entity: Entity, event: E) {
    //     self.
    // }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_world_thingy() {
        let entity_count = 10;
        let mut w = World::new();
        for i in 0..entity_count {
            w.create_entity();
        }
        println!("Bar");
        let mut inputs = Vec::new();
        inputs.resize(entity_count, 1u32);
        // Event input just being a vector is tricksy because it may not
        // be the same length as the entities vector.  It needs to be a particular
        // per-entity mapping of one kind or another.
        // Making it efficient so we don't need to always re-allocate a new vector
        // for returning new events will be a little wacky too.
        let results = w.run(inputs, |(x1, x2): (&u32, &u32)| {
            println!("Xs are is {:?} {:?}", x1, x2);
            x1 + x2
        });
        println!("Results are {:?}", results);
        let mut desired_results: Vec<u32> = Vec::new();
        desired_results.extend(&[2, 3, 4, 5, 6, 7, 8, 9, 10, 11][..]);
        assert_eq!(results, desired_results);
    }
}
<|endoftext|>


<commit_before>#![allow(missing_copy_implementations)]

use libc::{c_char, c_int, c_uint, c_void};

pub enum QrsEngine {}
pub enum QrsMetaObject {}
pub enum QObject {}
pub enum QVariant {}
pub enum QVariantList {}

#[repr(C)]
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum QrsVariantType {
    Invalid = 0,
    Bool,
    Int64,
    String
}

pub type SlotFunction = extern "C" fn(data: *mut c_void, id: c_int, args: *const *const QVariant);

extern "C" {
    pub fn qmlrs_create_engine() -> *mut QrsEngine;
    pub fn qmlrs_create_engine_headless() -> *mut QrsEngine;
    pub fn qmlrs_destroy_engine(engine: *mut QrsEngine);
    pub fn qmlrs_engine_load_url(engine: *mut QrsEngine, path: *const c_char, len: c_uint);
    pub fn qmlrs_engine_load_from_data(engine: *mut QrsEngine, data: *const c_char, len: c_uint);
    pub fn qmlrs_engine_invoke(engine: *mut QrsEngine, method: *const c_char, result: *mut QVariant,
                               args: *const QVariantList);
    pub fn qmlrs_engine_set_property(engine: *mut QrsEngine, name: *const c_char, len: c_uint,
                                     object: *mut QObject);

    pub fn qmlrs_variant_create() -> *mut QVariant;
    pub fn qmlrs_variant_destroy(v: *mut QVariant);
    pub fn qmlrs_variant_set_int64(var: *mut QVariant, x: i64);
    pub fn qmlrs_variant_set_bool(var: *mut QVariant, x: bool);
    pub fn qmlrs_variant_set_invalid(var: *mut QVariant);
    pub fn qmlrs_variant_set_string(var: *mut QVariant, len: c_uint, data: *const c_char);
    pub fn qmlrs_variant_get_type(var: *const QVariant) -> QrsVariantType;
    pub fn qmlrs_variant_get_int64(var: *const QVariant, x: *mut i64);
    pub fn qmlrs_variant_get_bool(var: *const QVariant, x: *mut bool);
    pub fn qmlrs_variant_get_string_length(var: *const QVariant, out: *mut c_uint);
    pub fn qmlrs_variant_get_string_data(var: *const QVariant, out: *mut c_char);

    pub fn qmlrs_varlist_create() -> *mut QVariantList;
    pub fn qmlrs_varlist_destroy(list: *mut QVariantList);
    pub fn qmlrs_varlist_push(list: *mut QVariantList) -> *mut QVariant;
    pub fn qmlrs_varlist_length(list: *const QVariantList) -> c_uint;
    pub fn qmlrs_varlist_get(list: *const QVariantList, i: c_uint) -> *mut QVariant;

    pub fn qmlrs_app_exec();

    pub fn qmlrs_metaobject_create() -> *mut QrsMetaObject;
    pub fn qmlrs_metaobject_destroy(mo: *mut QrsMetaObject);
    pub fn qmlrs_metaobject_add_slot(mo: *mut QrsMetaObject, name: *const c_char, name_len: c_uint,
                                     argc: c_uint);
    pub fn qmlrs_metaobject_add_signal(mo: *mut QrsMetaObject, name: *const c_char,
                                       name_len: c_uint, argc: c_uint);
    pub fn qmlrs_metaobject_instantiate(mo: *mut QrsMetaObject, fun: SlotFunction,
                                        data: *mut c_void) -> *mut QObject;

    pub fn qmlrs_object_emit_signal(obj: *mut QObject, id: c_uint);
    pub fn qmlrs_object_destroy(obj: *mut QObject);
}
<commit_msg>The order of values in QrsVariantType was different in ffi.rs and in libqmlrswrapper.cpp, causing the Factorial example to fail<commit_after>#![allow(missing_copy_implementations)]

use libc::{c_char, c_int, c_uint, c_void};

pub enum QrsEngine {}
pub enum QrsMetaObject {}
pub enum QObject {}
pub enum QVariant {}
pub enum QVariantList {}

#[repr(C)]
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub enum QrsVariantType {
    Invalid = 0,
    Int64,
    Bool,
    String
}

pub type SlotFunction = extern "C" fn(data: *mut c_void, id: c_int, args: *const *const QVariant);

extern "C" {
    pub fn qmlrs_create_engine() -> *mut QrsEngine;
    pub fn qmlrs_create_engine_headless() -> *mut QrsEngine;
    pub fn qmlrs_destroy_engine(engine: *mut QrsEngine);
    pub fn qmlrs_engine_load_url(engine: *mut QrsEngine, path: *const c_char, len: c_uint);
    pub fn qmlrs_engine_load_from_data(engine: *mut QrsEngine, data: *const c_char, len: c_uint);
    pub fn qmlrs_engine_invoke(engine: *mut QrsEngine, method: *const c_char, result: *mut QVariant,
                               args: *const QVariantList);
    pub fn qmlrs_engine_set_property(engine: *mut QrsEngine, name: *const c_char, len: c_uint,
                                     object: *mut QObject);

    pub fn qmlrs_variant_create() -> *mut QVariant;
    pub fn qmlrs_variant_destroy(v: *mut QVariant);
    pub fn qmlrs_variant_set_int64(var: *mut QVariant, x: i64);
    pub fn qmlrs_variant_set_bool(var: *mut QVariant, x: bool);
    pub fn qmlrs_variant_set_invalid(var: *mut QVariant);
    pub fn qmlrs_variant_set_string(var: *mut QVariant, len: c_uint, data: *const c_char);
    pub fn qmlrs_variant_get_type(var: *const QVariant) -> QrsVariantType;
    pub fn qmlrs_variant_get_int64(var: *const QVariant, x: *mut i64);
    pub fn qmlrs_variant_get_bool(var: *const QVariant, x: *mut bool);
    pub fn qmlrs_variant_get_string_length(var: *const QVariant, out: *mut c_uint);
    pub fn qmlrs_variant_get_string_data(var: *const QVariant, out: *mut c_char);

    pub fn qmlrs_varlist_create() -> *mut QVariantList;
    pub fn qmlrs_varlist_destroy(list: *mut QVariantList);
    pub fn qmlrs_varlist_push(list: *mut QVariantList) -> *mut QVariant;
    pub fn qmlrs_varlist_length(list: *const QVariantList) -> c_uint;
    pub fn qmlrs_varlist_get(list: *const QVariantList, i: c_uint) -> *mut QVariant;

    pub fn qmlrs_app_exec();

    pub fn qmlrs_metaobject_create() -> *mut QrsMetaObject;
    pub fn qmlrs_metaobject_destroy(mo: *mut QrsMetaObject);
    pub fn qmlrs_metaobject_add_slot(mo: *mut QrsMetaObject, name: *const c_char, name_len: c_uint,
                                     argc: c_uint);
    pub fn qmlrs_metaobject_add_signal(mo: *mut QrsMetaObject, name: *const c_char,
                                       name_len: c_uint, argc: c_uint);
    pub fn qmlrs_metaobject_instantiate(mo: *mut QrsMetaObject, fun: SlotFunction,
                                        data: *mut c_void) -> *mut QObject;

    pub fn qmlrs_object_emit_signal(obj: *mut QObject, id: c_uint);
    pub fn qmlrs_object_destroy(obj: *mut QObject);
}
<|endoftext|>