<commit_before>use std::{io, mem, ptr};
use std::fs::File;
use std::os::windows::io::{RawHandle, FromRawHandle};

use winapi;
use kernel32;

/// An event received from a child process
pub struct Event {
    pub process_id: u32,
    pub thread_id: u32,
    pub info: EventInfo,
}

impl !Send for Event {}

pub enum EventInfo {
    CreateProcess {
        file: Option<File>,
        process: RawHandle,
        main_thread: RawHandle,
        base: usize,
        start_address: usize,
    },
    ExitProcess { exit_code: u32 },

    CreateThread { thread: RawHandle, start_address: usize },
    ExitThread { exit_code: u32 },

    LoadDll { file: Option<File>, base: usize },
    UnloadDll { base: usize },

    OutputDebugString { data: usize, length: usize, unicode: bool },

    Exception {
        first_chance: bool,
        code: u32,
        address: usize,
    },

    Rip { error: u32, error_type: u32 },
}

impl Event {
    /// Retrieve the next `Event` from any child process attached to the curent thread
    pub fn wait_event() -> io::Result<Event> {
        unsafe {
            let mut event = mem::uninitialized();
            if kernel32::WaitForDebugEvent(&mut event, winapi::INFINITE) == winapi::FALSE {
                return Err(io::Error::last_os_error());
            }

            let process_id = event.dwProcessId;
            let thread_id = event.dwThreadId;

            use self::EventInfo::*;
            let info = match event.dwDebugEventCode {
                winapi::CREATE_PROCESS_DEBUG_EVENT => {
                    let cp = event.CreateProcessInfo();

                    let file = if cp.hFile != ptr::null_mut() {
                        Some(File::from_raw_handle(cp.hFile))
                    } else {
                        None
                    };
                    let process = cp.hProcess;
                    let main_thread = cp.hThread;
                    let base = cp.lpBaseOfImage as usize;
                    let start_address = mem::transmute(cp.lpStartAddress);

                    CreateProcess { file, process, main_thread, base, start_address }
                }
                winapi::EXIT_PROCESS_DEBUG_EVENT => {
                    let ep = event.ExitProcess();

                    let exit_code = ep.dwExitCode;
                    ExitProcess { exit_code }
                }

                winapi::CREATE_THREAD_DEBUG_EVENT => {
                    let ct = event.CreateThread();

                    let thread = ct.hThread;
                    let start_address = mem::transmute(ct.lpStartAddress);

                    CreateThread { thread, start_address }
                }
                winapi::EXIT_THREAD_DEBUG_EVENT => {
                    let et = event.ExitThread();

                    let exit_code = et.dwExitCode;
                    ExitThread { exit_code }
                }

                winapi::LOAD_DLL_DEBUG_EVENT => {
                    let ld = event.LoadDll();

                    let file = if ld.hFile != ptr::null_mut() {
                        Some(File::from_raw_handle(ld.hFile))
                    } else {
                        None
                    };

                    let base = ld.lpBaseOfDll as usize;

                    LoadDll { file, base }
                }
                winapi::UNLOAD_DLL_DEBUG_EVENT => {
                    let ud = event.UnloadDll();

                    let base = ud.lpBaseOfDll as usize;
                    UnloadDll { base }
                }

                winapi::OUTPUT_DEBUG_STRING_EVENT => {
                    let ds = event.DebugString();

                    let data = ds.lpDebugStringData as usize;
                    let length = ds.nDebugStringLength as usize;
                    let unicode = ds.fUnicode != 0;
                    OutputDebugString { data, length, unicode }
                }

                winapi::EXCEPTION_DEBUG_EVENT => {
                    let e = event.Exception();
                    let ref er = e.ExceptionRecord;

                    let first_chance = e.dwFirstChance != 0;
                    let code = er.ExceptionCode;
                    let address = er.ExceptionAddress as usize;
                    Exception { first_chance, code, address }
                },

                winapi::RIP_EVENT => {
                    let rip = event.RipInfo();

                    let error = rip.dwError;
                    let error_type = rip.dwType;
                    Rip { error, error_type }
                }

                _ => unreachable!()
            };

            Ok(Event { process_id, thread_id, info })
        }
    }

    /// Continue the child process that generated this event
    ///
    /// For `EventInfo::Exception` events, `handled` signifies whether the debugger handled
    /// the exception. If `handled == false`, the exception is passed on to the child process.
    pub fn continue_event(mut self, handled: bool) -> io::Result<()> {
        let status = if handled {
            winapi::DBG_CONTINUE
        } else {
            winapi::DBG_EXCEPTION_NOT_HANDLED
        } as winapi::DWORD;

        self.continue_status(status)?;

        mem::forget(self);
        Ok(())
    }

    fn continue_status(&mut self, status: winapi::DWORD) -> io::Result<()> {
        let Event { ref process_id, ref thread_id, .. } = *self;

        unsafe {
            if kernel32::ContinueDebugEvent(*process_id, *thread_id, status) == winapi::FALSE {
                return Err(io::Error::last_os_error());
            }
        }

        Ok(())
    }
}

impl Drop for Event {
    fn drop(&mut self) {
        let _ = self.continue_status(winapi::DBG_EXCEPTION_NOT_HANDLED as winapi::DWORD);
    }
}
<commit_msg>Don't leak file handles from debug events<commit_after>use std::{io, mem, ptr};
use std::fs::File;
use std::os::windows::io::{RawHandle, FromRawHandle};

use winapi;
use kernel32;

/// An event received from a child process
pub struct Event {
    pub process_id: u32,
    pub thread_id: u32,
    pub info: EventInfo,
}

impl !Send for Event {}

pub enum EventInfo {
    CreateProcess {
        file: Option<File>,
        process: RawHandle,
        main_thread: RawHandle,
        base: usize,
        start_address: usize,
    },
    ExitProcess { exit_code: u32 },

    CreateThread { thread: RawHandle, start_address: usize },
    ExitThread { exit_code: u32 },

    LoadDll { file: Option<File>, base: usize },
    UnloadDll { base: usize },

    OutputDebugString { data: usize, length: usize, unicode: bool },

    Exception {
        first_chance: bool,
        code: u32,
        address: usize,
    },

    Rip { error: u32, error_type: u32 },
}

impl Event {
    /// Retrieve the next `Event` from any child process attached to the curent thread
    pub fn wait_event() -> io::Result<Event> {
        unsafe {
            let mut event = mem::uninitialized();
            if kernel32::WaitForDebugEvent(&mut event, winapi::INFINITE) == winapi::FALSE {
                return Err(io::Error::last_os_error());
            }

            let process_id = event.dwProcessId;
            let thread_id = event.dwThreadId;

            use self::EventInfo::*;
            let info = match event.dwDebugEventCode {
                winapi::CREATE_PROCESS_DEBUG_EVENT => {
                    let cp = event.CreateProcessInfo();

                    let file = if cp.hFile != ptr::null_mut() {
                        Some(File::from_raw_handle(cp.hFile))
                    } else {
                        None
                    };
                    let process = cp.hProcess;
                    let main_thread = cp.hThread;
                    let base = cp.lpBaseOfImage as usize;
                    let start_address = mem::transmute(cp.lpStartAddress);

                    CreateProcess { file, process, main_thread, base, start_address }
                }
                winapi::EXIT_PROCESS_DEBUG_EVENT => {
                    let ep = event.ExitProcess();

                    let exit_code = ep.dwExitCode;
                    ExitProcess { exit_code }
                }

                winapi::CREATE_THREAD_DEBUG_EVENT => {
                    let ct = event.CreateThread();

                    let thread = ct.hThread;
                    let start_address = mem::transmute(ct.lpStartAddress);

                    CreateThread { thread, start_address }
                }
                winapi::EXIT_THREAD_DEBUG_EVENT => {
                    let et = event.ExitThread();

                    let exit_code = et.dwExitCode;
                    ExitThread { exit_code }
                }

                winapi::LOAD_DLL_DEBUG_EVENT => {
                    let ld = event.LoadDll();

                    let file = if ld.hFile != ptr::null_mut() {
                        Some(File::from_raw_handle(ld.hFile))
                    } else {
                        None
                    };

                    let base = ld.lpBaseOfDll as usize;

                    LoadDll { file, base }
                }
                winapi::UNLOAD_DLL_DEBUG_EVENT => {
                    let ud = event.UnloadDll();

                    let base = ud.lpBaseOfDll as usize;
                    UnloadDll { base }
                }

                winapi::OUTPUT_DEBUG_STRING_EVENT => {
                    let ds = event.DebugString();

                    let data = ds.lpDebugStringData as usize;
                    let length = ds.nDebugStringLength as usize;
                    let unicode = ds.fUnicode != 0;
                    OutputDebugString { data, length, unicode }
                }

                winapi::EXCEPTION_DEBUG_EVENT => {
                    let e = event.Exception();
                    let ref er = e.ExceptionRecord;

                    let first_chance = e.dwFirstChance != 0;
                    let code = er.ExceptionCode;
                    let address = er.ExceptionAddress as usize;
                    Exception { first_chance, code, address }
                },

                winapi::RIP_EVENT => {
                    let rip = event.RipInfo();

                    let error = rip.dwError;
                    let error_type = rip.dwType;
                    Rip { error, error_type }
                }

                _ => unreachable!()
            };

            Ok(Event { process_id, thread_id, info })
        }
    }

    /// Continue the child process that generated this event
    ///
    /// For `EventInfo::Exception` events, `handled` signifies whether the debugger handled
    /// the exception. If `handled == false`, the exception is passed on to the child process.
    pub fn continue_event(mut self, handled: bool) -> io::Result<()> {
        let status = if handled {
            winapi::DBG_CONTINUE
        } else {
            winapi::DBG_EXCEPTION_NOT_HANDLED
        } as winapi::DWORD;

        self.continue_status(status)?;

        Ok(())
    }

    fn continue_status(&mut self, status: winapi::DWORD) -> io::Result<()> {
        let Event { ref process_id, ref thread_id, .. } = *self;

        unsafe {
            if kernel32::ContinueDebugEvent(*process_id, *thread_id, status) == winapi::FALSE {
                return Err(io::Error::last_os_error());
            }
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::cmp::{max, min};

use ra_db::{SyntaxDatabase, Cancelable};
use ra_syntax::{
    AstNode, SyntaxNode, TextUnit, TextRange,
    SyntaxKind::FN_DEF,
    ast::{self, ArgListOwner, DocCommentsOwner},
};
use ra_editor::find_node_at_offset;

use crate::{FilePosition, CallInfo, db::RootDatabase};

/// Computes parameter information for the given call expression.
pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Cancelable<Option<CallInfo>> {
    let file = db.source_file(position.file_id);
    let syntax = file.syntax();

    // Find the calling expression and it's NameRef
    let calling_node = ctry!(FnCallNode::with_node(syntax, position.offset));
    let name_ref = ctry!(calling_node.name_ref());

    // Resolve the function's NameRef (NOTE: this isn't entirely accurate).
    let file_symbols = db.index_resolve(name_ref)?;
    let symbol = ctry!(file_symbols.into_iter().find(|it| it.ptr.kind() == FN_DEF));
    let fn_file = db.source_file(symbol.file_id);
    let fn_def = symbol.ptr.resolve(&fn_file);
    let fn_def = ast::FnDef::cast(&fn_def).unwrap();
    let mut call_info = ctry!(CallInfo::new(fn_def));
    // If we have a calling expression let's find which argument we are on
    let num_params = call_info.parameters.len();
    let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some();

    if num_params == 1 {
        if !has_self {
            call_info.active_parameter = Some(0);
        }
    } else if num_params > 1 {
        // Count how many parameters into the call we are.
        // TODO: This is best effort for now and should be fixed at some point.
        // It may be better to see where we are in the arg_list and then check
        // where offset is in that list (or beyond).
        // Revisit this after we get documentation comments in.
        if let Some(ref arg_list) = calling_node.arg_list() {
            let start = arg_list.syntax().range().start();

            let range_search = TextRange::from_to(start, position.offset);
            let mut commas: usize = arg_list
                .syntax()
                .text()
                .slice(range_search)
                .to_string()
                .matches(',')
                .count();

            // If we have a method call eat the first param since it's just self.
            if has_self {
                commas += 1;
            }

            call_info.active_parameter = Some(commas);
        }
    }

    Ok(Some(call_info))
}

enum FnCallNode<'a> {
    CallExpr(&'a ast::CallExpr),
    MethodCallExpr(&'a ast::MethodCallExpr),
}

impl<'a> FnCallNode<'a> {
    pub fn with_node(syntax: &'a SyntaxNode, offset: TextUnit) -> Option<FnCallNode<'a>> {
        if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) {
            return Some(FnCallNode::CallExpr(expr));
        }
        if let Some(expr) = find_node_at_offset::<ast::MethodCallExpr>(syntax, offset) {
            return Some(FnCallNode::MethodCallExpr(expr));
        }
        None
    }

    pub fn name_ref(&self) -> Option<&'a ast::NameRef> {
        match *self {
            FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()?.kind() {
                ast::ExprKind::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
                _ => return None,
            }),

            FnCallNode::MethodCallExpr(call_expr) => call_expr
                .syntax()
                .children()
                .filter_map(ast::NameRef::cast)
                .nth(0),
        }
    }

    pub fn arg_list(&self) -> Option<&'a ast::ArgList> {
        match *self {
            FnCallNode::CallExpr(expr) => expr.arg_list(),
            FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
        }
    }
}

impl CallInfo {
    fn new(node: &ast::FnDef) -> Option<Self> {
        let mut doc = None;

        // Strip the body out for the label.
        let mut label: String = if let Some(body) = node.body() {
            let body_range = body.syntax().range();
            let label: String = node
                .syntax()
                .children()
                .filter(|child| !child.range().is_subrange(&body_range))
                .map(|node| node.text().to_string())
                .collect();
            label
        } else {
            node.syntax().text().to_string()
        };

        if let Some((comment_range, docs)) = CallInfo::extract_doc_comments(node) {
            let comment_range = comment_range
                .checked_sub(node.syntax().range().start())
                .unwrap();
            let start = comment_range.start().to_usize();
            let end = comment_range.end().to_usize();

            // Remove the comment from the label
            label.replace_range(start..end, "");

            // Massage markdown
            let mut processed_lines = Vec::new();
            let mut in_code_block = false;
            for line in docs.lines() {
                if line.starts_with("```") {
                    in_code_block = !in_code_block;
                }

                let line = if in_code_block && line.starts_with("```") && !line.contains("rust") {
                    "```rust".into()
                } else {
                    line.to_string()
                };

                processed_lines.push(line);
            }

            if !processed_lines.is_empty() {
                doc = Some(processed_lines.join("\n"));
            }
        }

        Some(CallInfo {
            parameters: CallInfo::param_list(node),
            label: label.trim().to_owned(),
            doc,
            active_parameter: None,
        })
    }

    fn extract_doc_comments(node: &ast::FnDef) -> Option<(TextRange, String)> {
        if node.doc_comments().count() == 0 {
            return None;
        }

        let comment_text = node.doc_comment_text();

        let (begin, end) = node
            .doc_comments()
            .map(|comment| comment.syntax().range())
            .map(|range| (range.start().to_usize(), range.end().to_usize()))
            .fold((std::usize::MAX, std::usize::MIN), |acc, range| {
                (min(acc.0, range.0), max(acc.1, range.1))
            });

        let range = TextRange::from_to(TextUnit::from_usize(begin), TextUnit::from_usize(end));

        Some((range, comment_text))
    }

    fn param_list(node: &ast::FnDef) -> Vec<String> {
        let mut res = vec![];
        if let Some(param_list) = node.param_list() {
            if let Some(self_param) = param_list.self_param() {
                res.push(self_param.syntax().text().to_string())
            }

            // Maybe use param.pat here? See if we can just extract the name?
            //res.extend(param_list.params().map(|p| p.syntax().text().to_string()));
            res.extend(
                param_list
                    .params()
                    .filter_map(|p| p.pat())
                    .map(|pat| pat.syntax().text().to_string()),
            );
        }
        res
    }
}

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

    use crate::mock_analysis::single_file_with_position;

    fn call_info(text: &str) -> CallInfo {
        let (analysis, position) = single_file_with_position(text);
        analysis.call_info(position).unwrap().unwrap()
    }

    #[test]
    fn test_fn_signature_two_args_first() {
        let info = call_info(
            r#"fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(<|>3, ); }"#,
        );

        assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string()));
        assert_eq!(info.active_parameter, Some(0));
    }

    #[test]
    fn test_fn_signature_two_args_second() {
        let info = call_info(
            r#"fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(3, <|>); }"#,
        );

        assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string()));
        assert_eq!(info.active_parameter, Some(1));
    }

    #[test]
    fn test_fn_signature_for_impl() {
        let info = call_info(
            r#"struct F; impl F { pub fn new() { F{}} }
fn bar() {let _ : F = F::new(<|>);}"#,
        );

        assert_eq!(info.parameters, Vec::<String>::new());
        assert_eq!(info.active_parameter, None);
    }

    #[test]
    fn test_fn_signature_for_method_self() {
        let info = call_info(
            r#"struct F;
impl F {
    pub fn new() -> F{
        F{}
    }

    pub fn do_it(&self) {}
}

fn bar() {
    let f : F = F::new();
    f.do_it(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["&self".to_string()]);
        assert_eq!(info.active_parameter, None);
    }

    #[test]
    fn test_fn_signature_for_method_with_arg() {
        let info = call_info(
            r#"struct F;
impl F {
    pub fn new() -> F{
        F{}
    }

    pub fn do_it(&self, x: i32) {}
}

fn bar() {
    let f : F = F::new();
    f.do_it(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["&self".to_string(), "x".to_string()]);
        assert_eq!(info.active_parameter, Some(1));
    }

    #[test]
    fn test_fn_signature_with_docs_simple() {
        let info = call_info(
            r#"
/// test
// non-doc-comment
fn foo(j: u32) -> u32 {
    j
}

fn bar() {
    let _ = foo(<|>);
}
"#,
        );

        assert_eq!(info.parameters, vec!["j".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "fn foo(j: u32) -> u32".to_string());
        assert_eq!(info.doc, Some("test".into()));
    }

    #[test]
    fn test_fn_signature_with_docs() {
        let info = call_info(
            r#"
/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let five = 5;
///
/// assert_eq!(6, my_crate::add_one(5));
/// ```
pub fn add_one(x: i32) -> i32 {
    x + 1
}

pub fn do() {
    add_one(<|>
}"#,
        );

        assert_eq!(info.parameters, vec!["x".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
        assert_eq!(
            info.doc,
            Some(
                r#"Adds one to the number given.

# Examples

```rust
let five = 5;

assert_eq!(6, my_crate::add_one(5));
```"#
                    .into()
            )
        );
    }

    #[test]
    fn test_fn_signature_with_docs_impl() {
        let info = call_info(
            r#"
struct addr;
impl addr {
    /// Adds one to the number given.
    ///
    /// # Examples
    ///
    /// ```
    /// let five = 5;
    ///
    /// assert_eq!(6, my_crate::add_one(5));
    /// ```
    pub fn add_one(x: i32) -> i32 {
        x + 1
    }
}

pub fn do_it() {
    addr {};
    addr::add_one(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["x".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
        assert_eq!(
            info.doc,
            Some(
                r#"Adds one to the number given.

# Examples

```rust
let five = 5;

assert_eq!(6, my_crate::add_one(5));
```"#
                    .into()
            )
        );
    }

    #[test]
    fn test_fn_signature_with_docs_from_actix() {
        let info = call_info(
            r#"
pub trait WriteHandler<E>
where
    Self: Actor,
    Self::Context: ActorContext,
{
    /// Method is called when writer emits error.
    ///
    /// If this method returns `ErrorAction::Continue` writer processing
    /// continues otherwise stream processing stops.
    fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
        Running::Stop
    }

    /// Method is called when writer finishes.
    ///
    /// By default this method stops actor's `Context`.
    fn finished(&mut self, ctx: &mut Self::Context) {
        ctx.stop()
    }
}

pub fn foo() {
    WriteHandler r;
    r.finished(<|>);
}

"#,
        );

        assert_eq!(
            info.parameters,
            vec!["&mut self".to_string(), "ctx".to_string()]
        );
        assert_eq!(info.active_parameter, Some(1));
        assert_eq!(
            info.doc,
            Some(
                r#"Method is called when writer finishes.

By default this method stops actor's `Context`."#
                    .into()
            )
        );
    }

}
<commit_msg>simplify<commit_after>use std::cmp::{max, min};

use ra_db::{SyntaxDatabase, Cancelable};
use ra_syntax::{
    AstNode, SyntaxNode, TextUnit, TextRange,
    SyntaxKind::FN_DEF,
    ast::{self, ArgListOwner, DocCommentsOwner},
};
use ra_editor::find_node_at_offset;

use crate::{FilePosition, CallInfo, db::RootDatabase};

/// Computes parameter information for the given call expression.
pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Cancelable<Option<CallInfo>> {
    let file = db.source_file(position.file_id);
    let syntax = file.syntax();

    // Find the calling expression and it's NameRef
    let calling_node = ctry!(FnCallNode::with_node(syntax, position.offset));
    let name_ref = ctry!(calling_node.name_ref());

    // Resolve the function's NameRef (NOTE: this isn't entirely accurate).
    let file_symbols = db.index_resolve(name_ref)?;
    let symbol = ctry!(file_symbols.into_iter().find(|it| it.ptr.kind() == FN_DEF));
    let fn_file = db.source_file(symbol.file_id);
    let fn_def = symbol.ptr.resolve(&fn_file);
    let fn_def = ast::FnDef::cast(&fn_def).unwrap();
    let mut call_info = ctry!(CallInfo::new(fn_def));
    // If we have a calling expression let's find which argument we are on
    let num_params = call_info.parameters.len();
    let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some();

    if num_params == 1 {
        if !has_self {
            call_info.active_parameter = Some(0);
        }
    } else if num_params > 1 {
        // Count how many parameters into the call we are.
        // TODO: This is best effort for now and should be fixed at some point.
        // It may be better to see where we are in the arg_list and then check
        // where offset is in that list (or beyond).
        // Revisit this after we get documentation comments in.
        if let Some(ref arg_list) = calling_node.arg_list() {
            let start = arg_list.syntax().range().start();

            let range_search = TextRange::from_to(start, position.offset);
            let mut commas: usize = arg_list
                .syntax()
                .text()
                .slice(range_search)
                .to_string()
                .matches(',')
                .count();

            // If we have a method call eat the first param since it's just self.
            if has_self {
                commas += 1;
            }

            call_info.active_parameter = Some(commas);
        }
    }

    Ok(Some(call_info))
}

enum FnCallNode<'a> {
    CallExpr(&'a ast::CallExpr),
    MethodCallExpr(&'a ast::MethodCallExpr),
}

impl<'a> FnCallNode<'a> {
    pub fn with_node(syntax: &'a SyntaxNode, offset: TextUnit) -> Option<FnCallNode<'a>> {
        if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) {
            return Some(FnCallNode::CallExpr(expr));
        }
        if let Some(expr) = find_node_at_offset::<ast::MethodCallExpr>(syntax, offset) {
            return Some(FnCallNode::MethodCallExpr(expr));
        }
        None
    }

    pub fn name_ref(&self) -> Option<&'a ast::NameRef> {
        match *self {
            FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()?.kind() {
                ast::ExprKind::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
                _ => return None,
            }),

            FnCallNode::MethodCallExpr(call_expr) => call_expr
                .syntax()
                .children()
                .filter_map(ast::NameRef::cast)
                .nth(0),
        }
    }

    pub fn arg_list(&self) -> Option<&'a ast::ArgList> {
        match *self {
            FnCallNode::CallExpr(expr) => expr.arg_list(),
            FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
        }
    }
}

impl CallInfo {
    fn new(node: &ast::FnDef) -> Option<Self> {
        let mut doc = None;

        // Strip the body out for the label.
        let mut label: String = if let Some(body) = node.body() {
            let body_range = body.syntax().range();
            let label: String = node
                .syntax()
                .children()
                .filter(|child| !child.range().is_subrange(&body_range))
                .map(|node| node.text().to_string())
                .collect();
            label
        } else {
            node.syntax().text().to_string()
        };

        if let Some((comment_range, docs)) = extract_doc_comments(node) {
            let comment_range = comment_range
                .checked_sub(node.syntax().range().start())
                .unwrap();
            let start = comment_range.start().to_usize();
            let end = comment_range.end().to_usize();

            // Remove the comment from the label
            label.replace_range(start..end, "");

            // Massage markdown
            let mut processed_lines = Vec::new();
            let mut in_code_block = false;
            for line in docs.lines() {
                if line.starts_with("```") {
                    in_code_block = !in_code_block;
                }

                let line = if in_code_block && line.starts_with("```") && !line.contains("rust") {
                    "```rust".into()
                } else {
                    line.to_string()
                };

                processed_lines.push(line);
            }

            if !processed_lines.is_empty() {
                doc = Some(processed_lines.join("\n"));
            }
        }

        Some(CallInfo {
            parameters: param_list(node),
            label: label.trim().to_owned(),
            doc,
            active_parameter: None,
        })
    }
}

fn extract_doc_comments(node: &ast::FnDef) -> Option<(TextRange, String)> {
    if node.doc_comments().count() == 0 {
        return None;
    }

    let comment_text = node.doc_comment_text();

    let (begin, end) = node
        .doc_comments()
        .map(|comment| comment.syntax().range())
        .map(|range| (range.start().to_usize(), range.end().to_usize()))
        .fold((std::usize::MAX, std::usize::MIN), |acc, range| {
            (min(acc.0, range.0), max(acc.1, range.1))
        });

    let range = TextRange::from_to(TextUnit::from_usize(begin), TextUnit::from_usize(end));

    Some((range, comment_text))
}

fn param_list(node: &ast::FnDef) -> Vec<String> {
    let mut res = vec![];
    if let Some(param_list) = node.param_list() {
        if let Some(self_param) = param_list.self_param() {
            res.push(self_param.syntax().text().to_string())
        }

        // Maybe use param.pat here? See if we can just extract the name?
        //res.extend(param_list.params().map(|p| p.syntax().text().to_string()));
        res.extend(
            param_list
                .params()
                .filter_map(|p| p.pat())
                .map(|pat| pat.syntax().text().to_string()),
        );
    }
    res
}

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

    use crate::mock_analysis::single_file_with_position;

    fn call_info(text: &str) -> CallInfo {
        let (analysis, position) = single_file_with_position(text);
        analysis.call_info(position).unwrap().unwrap()
    }

    #[test]
    fn test_fn_signature_two_args_first() {
        let info = call_info(
            r#"fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(<|>3, ); }"#,
        );

        assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string()));
        assert_eq!(info.active_parameter, Some(0));
    }

    #[test]
    fn test_fn_signature_two_args_second() {
        let info = call_info(
            r#"fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(3, <|>); }"#,
        );

        assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string()));
        assert_eq!(info.active_parameter, Some(1));
    }

    #[test]
    fn test_fn_signature_for_impl() {
        let info = call_info(
            r#"struct F; impl F { pub fn new() { F{}} }
fn bar() {let _ : F = F::new(<|>);}"#,
        );

        assert_eq!(info.parameters, Vec::<String>::new());
        assert_eq!(info.active_parameter, None);
    }

    #[test]
    fn test_fn_signature_for_method_self() {
        let info = call_info(
            r#"struct F;
impl F {
    pub fn new() -> F{
        F{}
    }

    pub fn do_it(&self) {}
}

fn bar() {
    let f : F = F::new();
    f.do_it(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["&self".to_string()]);
        assert_eq!(info.active_parameter, None);
    }

    #[test]
    fn test_fn_signature_for_method_with_arg() {
        let info = call_info(
            r#"struct F;
impl F {
    pub fn new() -> F{
        F{}
    }

    pub fn do_it(&self, x: i32) {}
}

fn bar() {
    let f : F = F::new();
    f.do_it(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["&self".to_string(), "x".to_string()]);
        assert_eq!(info.active_parameter, Some(1));
    }

    #[test]
    fn test_fn_signature_with_docs_simple() {
        let info = call_info(
            r#"
/// test
// non-doc-comment
fn foo(j: u32) -> u32 {
    j
}

fn bar() {
    let _ = foo(<|>);
}
"#,
        );

        assert_eq!(info.parameters, vec!["j".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "fn foo(j: u32) -> u32".to_string());
        assert_eq!(info.doc, Some("test".into()));
    }

    #[test]
    fn test_fn_signature_with_docs() {
        let info = call_info(
            r#"
/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let five = 5;
///
/// assert_eq!(6, my_crate::add_one(5));
/// ```
pub fn add_one(x: i32) -> i32 {
    x + 1
}

pub fn do() {
    add_one(<|>
}"#,
        );

        assert_eq!(info.parameters, vec!["x".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
        assert_eq!(
            info.doc,
            Some(
                r#"Adds one to the number given.

# Examples

```rust
let five = 5;

assert_eq!(6, my_crate::add_one(5));
```"#
                    .into()
            )
        );
    }

    #[test]
    fn test_fn_signature_with_docs_impl() {
        let info = call_info(
            r#"
struct addr;
impl addr {
    /// Adds one to the number given.
    ///
    /// # Examples
    ///
    /// ```
    /// let five = 5;
    ///
    /// assert_eq!(6, my_crate::add_one(5));
    /// ```
    pub fn add_one(x: i32) -> i32 {
        x + 1
    }
}

pub fn do_it() {
    addr {};
    addr::add_one(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["x".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
        assert_eq!(
            info.doc,
            Some(
                r#"Adds one to the number given.

# Examples

```rust
let five = 5;

assert_eq!(6, my_crate::add_one(5));
```"#
                    .into()
            )
        );
    }

    #[test]
    fn test_fn_signature_with_docs_from_actix() {
        let info = call_info(
            r#"
pub trait WriteHandler<E>
where
    Self: Actor,
    Self::Context: ActorContext,
{
    /// Method is called when writer emits error.
    ///
    /// If this method returns `ErrorAction::Continue` writer processing
    /// continues otherwise stream processing stops.
    fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
        Running::Stop
    }

    /// Method is called when writer finishes.
    ///
    /// By default this method stops actor's `Context`.
    fn finished(&mut self, ctx: &mut Self::Context) {
        ctx.stop()
    }
}

pub fn foo() {
    WriteHandler r;
    r.finished(<|>);
}

"#,
        );

        assert_eq!(
            info.parameters,
            vec!["&mut self".to_string(), "ctx".to_string()]
        );
        assert_eq!(info.active_parameter, Some(1));
        assert_eq!(
            info.doc,
            Some(
                r#"Method is called when writer finishes.

By default this method stops actor's `Context`."#
                    .into()
            )
        );
    }

}
<|endoftext|>


<commit_before>/* AES ECB/CBC mode oracle
 *
 * Dmitry Vasiliev <dima@hlabs.org>
 */

extern crate aes_lib;

use std::rand;

use aes_lib::{AES_BLOCK_SIZE, encrypt_aes_ecb, encrypt_aes_cbc};

fn random_bytes(len: uint) -> Vec<u8> {
    Vec::from_fn(len, |_| rand::random::<u8>())
}

fn random_uint(low: uint, high: uint) -> uint {
    // FIXME: Why rand::task_rng().gen_range::<uint>(low, high) doesn't work?
    (rand::random::<uint>() % (high - low)) + low
}

fn random_bool() -> bool {
    rand::random::<bool>()
}

fn aes_encrypt() -> (Vec<u8>, bool) {
    let key = random_bytes(AES_BLOCK_SIZE);
    let data = random_bytes(AES_BLOCK_SIZE * random_uint(20, 60)
                            + random_uint(0, AES_BLOCK_SIZE));
    let use_ecb = random_bool();
    let encrypted = if use_ecb {
        encrypt_aes_ecb(data.as_slice(), key.as_slice())
    } else {
        let iv = random_bytes(AES_BLOCK_SIZE);
        encrypt_aes_cbc(data.as_slice(), key.as_slice(), iv.as_slice())
    };
    (encrypted, use_ecb)
}

fn main() {
    let (encrypted, use_ecb) = aes_encrypt();
    let mode = if use_ecb {"ECB"} else {"CBC"};
    println!("Mode: {} Enc: {}", mode, encrypted);
}
<commit_msg>Fix task_rng().gen_range()<commit_after>/* AES ECB/CBC mode oracle
 *
 * Dmitry Vasiliev <dima@hlabs.org>
 */

extern crate aes_lib;

use std::rand::{random, task_rng, Rng};

use aes_lib::{AES_BLOCK_SIZE, encrypt_aes_ecb, encrypt_aes_cbc};

fn random_bytes(len: uint) -> Vec<u8> {
    Vec::from_fn(len, |_| random::<u8>())
}

fn random_uint(low: uint, high: uint) -> uint {
    task_rng().gen_range(low, high)
}

fn random_bool() -> bool {
    random::<bool>()
}

fn aes_encrypt() -> (Vec<u8>, bool) {
    let key = random_bytes(AES_BLOCK_SIZE);
    let data = random_bytes(AES_BLOCK_SIZE * random_uint(30, 80)
                            + random_uint(0, AES_BLOCK_SIZE));
    let use_ecb = random_bool();
    let encrypted = if use_ecb {
        encrypt_aes_ecb(data.as_slice(), key.as_slice())
    } else {
        let iv = random_bytes(AES_BLOCK_SIZE);
        encrypt_aes_cbc(data.as_slice(), key.as_slice(), iv.as_slice())
    };
    (encrypted, use_ecb)
}

fn main() {
    let (encrypted, use_ecb) = aes_encrypt();
    let mode = if use_ecb {"ECB"} else {"CBC"};
    println!("Mode: {} Enc: {}", mode, encrypted);
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use canvas_traits::CanvasMsg;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::WebGLFramebufferBinding;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use dom::bindings::js::{HeapGCValue, JS, Root};
use dom::bindings::reflector::reflect_dom_object;
use dom::globalscope::GlobalScope;
use dom::webglobject::WebGLObject;
use dom::webglrenderbuffer::WebGLRenderbuffer;
use dom::webgltexture::WebGLTexture;
use ipc_channel::ipc::{self, IpcSender};
use std::cell::Cell;
use webrender_traits::{WebGLCommand, WebGLFramebufferBindingRequest, WebGLFramebufferId, WebGLResult, WebGLError};

#[must_root]
#[derive(JSTraceable, Clone, HeapSizeOf)]
enum WebGLFramebufferAttachment {
    Renderbuffer(JS<WebGLRenderbuffer>),
    Texture(JS<WebGLTexture>),
}

impl HeapGCValue for WebGLFramebufferAttachment {}

#[dom_struct]
pub struct WebGLFramebuffer {
    webgl_object: WebGLObject,
    id: WebGLFramebufferId,
    /// target can only be gl::FRAMEBUFFER at the moment
    target: Cell<Option<u32>>,
    is_deleted: Cell<bool>,
    status: Cell<u32>,
    #[ignore_heap_size_of = "Defined in ipc-channel"]
    renderer: IpcSender<CanvasMsg>,

    // The attachment points for textures and renderbuffers on this
    // FBO.
    color: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    depth: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    stencil: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    depthstencil: DOMRefCell<Option<WebGLFramebufferAttachment>>,
}

impl WebGLFramebuffer {
    fn new_inherited(renderer: IpcSender<CanvasMsg>,
                     id: WebGLFramebufferId)
                     -> WebGLFramebuffer {
        WebGLFramebuffer {
            webgl_object: WebGLObject::new_inherited(),
            id: id,
            target: Cell::new(None),
            is_deleted: Cell::new(false),
            renderer: renderer,
            status: Cell::new(constants::FRAMEBUFFER_UNSUPPORTED),
            color: DOMRefCell::new(None),
            depth: DOMRefCell::new(None),
            stencil: DOMRefCell::new(None),
            depthstencil: DOMRefCell::new(None),
        }
    }

    pub fn maybe_new(global: &GlobalScope, renderer: IpcSender<CanvasMsg>)
                     -> Option<Root<WebGLFramebuffer>> {
        let (sender, receiver) = ipc::channel().unwrap();
        renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateFramebuffer(sender))).unwrap();

        let result = receiver.recv().unwrap();
        result.map(|fb_id| WebGLFramebuffer::new(global, renderer, fb_id))
    }

    pub fn new(global: &GlobalScope,
               renderer: IpcSender<CanvasMsg>,
               id: WebGLFramebufferId)
               -> Root<WebGLFramebuffer> {
        reflect_dom_object(box WebGLFramebuffer::new_inherited(renderer, id),
                           global,
                           WebGLFramebufferBinding::Wrap)
    }
}

impl WebGLFramebuffer {
    pub fn id(&self) -> WebGLFramebufferId {
        self.id
    }

    pub fn bind(&self, target: u32) {
        self.target.set(Some(target));
        let cmd = WebGLCommand::BindFramebuffer(target, WebGLFramebufferBindingRequest::Explicit(self.id));
        self.renderer.send(CanvasMsg::WebGL(cmd)).unwrap();
    }

    pub fn delete(&self) {
        if !self.is_deleted.get() {
            self.is_deleted.set(true);
            let _ = self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DeleteFramebuffer(self.id)));
        }
    }

    pub fn is_deleted(&self) -> bool {
        self.is_deleted.get()
    }

    fn update_status(&self) {
        let has_c = self.color.borrow().is_some();
        let has_z = self.depth.borrow().is_some();
        let has_s = self.stencil.borrow().is_some();
        let has_zs = self.depthstencil.borrow().is_some();

        // From the WebGL spec, 6.6 ("Framebuffer Object Attachments"):
        //
        //    "In the WebGL API, it is an error to concurrently attach
        //     renderbuffers to the following combinations of
        //     attachment points:
        //
        //     DEPTH_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
        //     STENCIL_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
        //     DEPTH_ATTACHMENT + STENCIL_ATTACHMENT
        //
        //     If any of the constraints above are violated, then:
        //
        //     checkFramebufferStatus must return FRAMEBUFFER_UNSUPPORTED."
        if (has_zs && (has_z || has_s)) ||
            (has_z && has_s) {
            self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
            return;
        }

        if has_c || has_z || has_zs || has_s {
            self.status.set(constants::FRAMEBUFFER_COMPLETE);
        } else {
            self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
        }
    }

    pub fn check_status(&self) -> u32 {
        return self.status.get();
    }

    pub fn renderbuffer(&self, attachment: u32, rb: Option<&WebGLRenderbuffer>) -> WebGLResult<()> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return Err(WebGLError::InvalidEnum),
        };

        let rb_id = match rb {
            Some(rb) => {
                *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Renderbuffer(JS::from_ref(rb)));
                Some(rb.id())
            }

            _ => {
                *binding.borrow_mut() = None;
                None
            }
        };

        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::FramebufferRenderbuffer(constants::FRAMEBUFFER,
                                                                                attachment,
                                                                                constants::RENDERBUFFER,
                                                                                rb_id))).unwrap();

        self.update_status();
        Ok(())
    }

    pub fn texture2d(&self, attachment: u32, textarget: u32, texture: Option<&WebGLTexture>,
                     level: i32) -> WebGLResult<()> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return Err(WebGLError::InvalidEnum),
        };

        let tex_id = match texture {
            // Note, from the GLES 2.0.25 spec, page 113:
            //      "If texture is zero, then textarget and level are ignored."
            Some(texture) => {
                *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Texture(JS::from_ref(texture)));

                // From the GLES 2.0.25 spec, page 113:
                //
                //     "level specifies the mipmap level of the texture image
                //      to be attached to the framebuffer and must be
                //      0. Otherwise, INVALID_VALUE is generated."
                if level != 0 {
                    return Err(WebGLError::InvalidValue);
                }

                //     "If texture is not zero, then texture must either
                //      name an existing texture object with an target of
                //      textarget, or texture must name an existing cube
                //      map texture and textarget must be one of:
                //      TEXTURE_CUBE_MAP_POSITIVE_X,
                //      TEXTURE_CUBE_MAP_POSITIVE_Y,
                //      TEXTURE_CUBE_MAP_POSITIVE_Z,
                //      TEXTURE_CUBE_MAP_NEGATIVE_X,
                //      TEXTURE_CUBE_MAP_NEGATIVE_Y, or
                //      TEXTURE_CUBE_MAP_NEGATIVE_Z. Otherwise,
                //      INVALID_OPERATION is generated."
                let is_cube = match textarget {
                    constants::TEXTURE_2D => false,

                    constants::TEXTURE_CUBE_MAP_POSITIVE_X => true,
                    constants::TEXTURE_CUBE_MAP_POSITIVE_Y => true,
                    constants::TEXTURE_CUBE_MAP_POSITIVE_Z => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_X => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => true,

                    _ => return Err(WebGLError::InvalidEnum),
                };

                match texture.target() {
                    Some(constants::TEXTURE_CUBE_MAP) if is_cube => {}
                    Some(_) if !is_cube => {}
                    _ => return Err(WebGLError::InvalidOperation),
                }

                Some(texture.id())
            }

            _ => {
                *binding.borrow_mut() = None;
                self.update_status();
                None
            }
        };

        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::FramebufferTexture2D(constants::FRAMEBUFFER,
                                                                               attachment,
                                                                               textarget,
                                                                               tex_id,
                                                                               level))).unwrap();

        self.update_status();
        Ok(())
    }

    pub fn detach_renderbuffer(&self, rb: &WebGLRenderbuffer) {
        let attachments = [&self.color,
                           &self.depth,
                           &self.stencil,
                           &self.depthstencil];

        for attachment in &attachments {
            let matched = {
                match *attachment.borrow() {
                    Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb))
                        if rb.id() == att_rb.id() => true,
                    _ => false,
                }
            };

            if matched {
                *attachment.borrow_mut() = None;
                self.update_status();
            }
        }
    }

    pub fn detach_texture(&self, texture: &WebGLTexture) {
        let attachments = [&self.color,
                           &self.depth,
                           &self.stencil,
                           &self.depthstencil];

        for attachment in &attachments {
            let matched = {
                match *attachment.borrow() {
                    Some(WebGLFramebufferAttachment::Texture(ref att_texture))
                        if texture.id() == att_texture.id() => true,
                    _ => false,
                }
            };

            if matched {
                *attachment.borrow_mut() = None;
            }
        }
    }

    pub fn target(&self) -> Option<u32> {
        self.target.get()
    }
}

impl Drop for WebGLFramebuffer {
    fn drop(&mut self) {
        self.delete();
    }
}
<commit_msg>webgl: Re-check FBO status on binding an FBO.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use canvas_traits::CanvasMsg;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::WebGLFramebufferBinding;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use dom::bindings::js::{HeapGCValue, JS, Root};
use dom::bindings::reflector::reflect_dom_object;
use dom::globalscope::GlobalScope;
use dom::webglobject::WebGLObject;
use dom::webglrenderbuffer::WebGLRenderbuffer;
use dom::webgltexture::WebGLTexture;
use ipc_channel::ipc::{self, IpcSender};
use std::cell::Cell;
use webrender_traits::{WebGLCommand, WebGLFramebufferBindingRequest, WebGLFramebufferId, WebGLResult, WebGLError};

#[must_root]
#[derive(JSTraceable, Clone, HeapSizeOf)]
enum WebGLFramebufferAttachment {
    Renderbuffer(JS<WebGLRenderbuffer>),
    Texture(JS<WebGLTexture>),
}

impl HeapGCValue for WebGLFramebufferAttachment {}

#[dom_struct]
pub struct WebGLFramebuffer {
    webgl_object: WebGLObject,
    id: WebGLFramebufferId,
    /// target can only be gl::FRAMEBUFFER at the moment
    target: Cell<Option<u32>>,
    is_deleted: Cell<bool>,
    status: Cell<u32>,
    #[ignore_heap_size_of = "Defined in ipc-channel"]
    renderer: IpcSender<CanvasMsg>,

    // The attachment points for textures and renderbuffers on this
    // FBO.
    color: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    depth: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    stencil: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    depthstencil: DOMRefCell<Option<WebGLFramebufferAttachment>>,
}

impl WebGLFramebuffer {
    fn new_inherited(renderer: IpcSender<CanvasMsg>,
                     id: WebGLFramebufferId)
                     -> WebGLFramebuffer {
        WebGLFramebuffer {
            webgl_object: WebGLObject::new_inherited(),
            id: id,
            target: Cell::new(None),
            is_deleted: Cell::new(false),
            renderer: renderer,
            status: Cell::new(constants::FRAMEBUFFER_UNSUPPORTED),
            color: DOMRefCell::new(None),
            depth: DOMRefCell::new(None),
            stencil: DOMRefCell::new(None),
            depthstencil: DOMRefCell::new(None),
        }
    }

    pub fn maybe_new(global: &GlobalScope, renderer: IpcSender<CanvasMsg>)
                     -> Option<Root<WebGLFramebuffer>> {
        let (sender, receiver) = ipc::channel().unwrap();
        renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateFramebuffer(sender))).unwrap();

        let result = receiver.recv().unwrap();
        result.map(|fb_id| WebGLFramebuffer::new(global, renderer, fb_id))
    }

    pub fn new(global: &GlobalScope,
               renderer: IpcSender<CanvasMsg>,
               id: WebGLFramebufferId)
               -> Root<WebGLFramebuffer> {
        reflect_dom_object(box WebGLFramebuffer::new_inherited(renderer, id),
                           global,
                           WebGLFramebufferBinding::Wrap)
    }
}

impl WebGLFramebuffer {
    pub fn id(&self) -> WebGLFramebufferId {
        self.id
    }

    pub fn bind(&self, target: u32) {
        // Update the framebuffer status on binding.  It may have
        // changed if its attachments were resized or deleted while
        // we've been unbound.
        self.update_status();

        self.target.set(Some(target));
        let cmd = WebGLCommand::BindFramebuffer(target, WebGLFramebufferBindingRequest::Explicit(self.id));
        self.renderer.send(CanvasMsg::WebGL(cmd)).unwrap();
    }

    pub fn delete(&self) {
        if !self.is_deleted.get() {
            self.is_deleted.set(true);
            let _ = self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DeleteFramebuffer(self.id)));
        }
    }

    pub fn is_deleted(&self) -> bool {
        self.is_deleted.get()
    }

    fn update_status(&self) {
        let has_c = self.color.borrow().is_some();
        let has_z = self.depth.borrow().is_some();
        let has_s = self.stencil.borrow().is_some();
        let has_zs = self.depthstencil.borrow().is_some();

        // From the WebGL spec, 6.6 ("Framebuffer Object Attachments"):
        //
        //    "In the WebGL API, it is an error to concurrently attach
        //     renderbuffers to the following combinations of
        //     attachment points:
        //
        //     DEPTH_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
        //     STENCIL_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
        //     DEPTH_ATTACHMENT + STENCIL_ATTACHMENT
        //
        //     If any of the constraints above are violated, then:
        //
        //     checkFramebufferStatus must return FRAMEBUFFER_UNSUPPORTED."
        if (has_zs && (has_z || has_s)) ||
            (has_z && has_s) {
            self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
            return;
        }

        if has_c || has_z || has_zs || has_s {
            self.status.set(constants::FRAMEBUFFER_COMPLETE);
        } else {
            self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
        }
    }

    pub fn check_status(&self) -> u32 {
        return self.status.get();
    }

    pub fn renderbuffer(&self, attachment: u32, rb: Option<&WebGLRenderbuffer>) -> WebGLResult<()> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return Err(WebGLError::InvalidEnum),
        };

        let rb_id = match rb {
            Some(rb) => {
                *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Renderbuffer(JS::from_ref(rb)));
                Some(rb.id())
            }

            _ => {
                *binding.borrow_mut() = None;
                None
            }
        };

        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::FramebufferRenderbuffer(constants::FRAMEBUFFER,
                                                                                attachment,
                                                                                constants::RENDERBUFFER,
                                                                                rb_id))).unwrap();

        self.update_status();
        Ok(())
    }

    pub fn texture2d(&self, attachment: u32, textarget: u32, texture: Option<&WebGLTexture>,
                     level: i32) -> WebGLResult<()> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return Err(WebGLError::InvalidEnum),
        };

        let tex_id = match texture {
            // Note, from the GLES 2.0.25 spec, page 113:
            //      "If texture is zero, then textarget and level are ignored."
            Some(texture) => {
                *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Texture(JS::from_ref(texture)));

                // From the GLES 2.0.25 spec, page 113:
                //
                //     "level specifies the mipmap level of the texture image
                //      to be attached to the framebuffer and must be
                //      0. Otherwise, INVALID_VALUE is generated."
                if level != 0 {
                    return Err(WebGLError::InvalidValue);
                }

                //     "If texture is not zero, then texture must either
                //      name an existing texture object with an target of
                //      textarget, or texture must name an existing cube
                //      map texture and textarget must be one of:
                //      TEXTURE_CUBE_MAP_POSITIVE_X,
                //      TEXTURE_CUBE_MAP_POSITIVE_Y,
                //      TEXTURE_CUBE_MAP_POSITIVE_Z,
                //      TEXTURE_CUBE_MAP_NEGATIVE_X,
                //      TEXTURE_CUBE_MAP_NEGATIVE_Y, or
                //      TEXTURE_CUBE_MAP_NEGATIVE_Z. Otherwise,
                //      INVALID_OPERATION is generated."
                let is_cube = match textarget {
                    constants::TEXTURE_2D => false,

                    constants::TEXTURE_CUBE_MAP_POSITIVE_X => true,
                    constants::TEXTURE_CUBE_MAP_POSITIVE_Y => true,
                    constants::TEXTURE_CUBE_MAP_POSITIVE_Z => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_X => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => true,

                    _ => return Err(WebGLError::InvalidEnum),
                };

                match texture.target() {
                    Some(constants::TEXTURE_CUBE_MAP) if is_cube => {}
                    Some(_) if !is_cube => {}
                    _ => return Err(WebGLError::InvalidOperation),
                }

                Some(texture.id())
            }

            _ => {
                *binding.borrow_mut() = None;
                self.update_status();
                None
            }
        };

        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::FramebufferTexture2D(constants::FRAMEBUFFER,
                                                                               attachment,
                                                                               textarget,
                                                                               tex_id,
                                                                               level))).unwrap();

        self.update_status();
        Ok(())
    }

    pub fn detach_renderbuffer(&self, rb: &WebGLRenderbuffer) {
        let attachments = [&self.color,
                           &self.depth,
                           &self.stencil,
                           &self.depthstencil];

        for attachment in &attachments {
            let matched = {
                match *attachment.borrow() {
                    Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb))
                        if rb.id() == att_rb.id() => true,
                    _ => false,
                }
            };

            if matched {
                *attachment.borrow_mut() = None;
                self.update_status();
            }
        }
    }

    pub fn detach_texture(&self, texture: &WebGLTexture) {
        let attachments = [&self.color,
                           &self.depth,
                           &self.stencil,
                           &self.depthstencil];

        for attachment in &attachments {
            let matched = {
                match *attachment.borrow() {
                    Some(WebGLFramebufferAttachment::Texture(ref att_texture))
                        if texture.id() == att_texture.id() => true,
                    _ => false,
                }
            };

            if matched {
                *attachment.borrow_mut() = None;
            }
        }
    }

    pub fn target(&self) -> Option<u32> {
        self.target.get()
    }
}

impl Drop for WebGLFramebuffer {
    fn drop(&mut self) {
        self.delete();
    }
}
<|endoftext|>


<commit_before>#![allow(clippy::eq_op)]

mod common;

use self::common::*;
use anyhow::ensure;

#[test]
fn test_messages() {
    assert_eq!("oh no!", bail_literal().unwrap_err().to_string());
    assert_eq!("oh no!", bail_fmt().unwrap_err().to_string());
    assert_eq!("oh no!", bail_error().unwrap_err().to_string());
}

#[test]
fn test_ensure() {
    let f = || {
        ensure!(1 + 1 == 2, "This is correct");
        Ok(())
    };
    assert!(f().is_ok());

    let v = 1;
    let f = || {
        ensure!(v + v == 2, "This is correct, v: {}", v);
        Ok(())
    };
    assert!(f().is_ok());

    let f = || {
        ensure!(v + v == 1, "This is not correct, v: {}", v);
        Ok(())
    };
    assert!(f().is_err());
}
<commit_msg>Test 1-argument ensure<commit_after>#![allow(clippy::eq_op)]

mod common;

use self::common::*;
use anyhow::ensure;

#[test]
fn test_messages() {
    assert_eq!("oh no!", bail_literal().unwrap_err().to_string());
    assert_eq!("oh no!", bail_fmt().unwrap_err().to_string());
    assert_eq!("oh no!", bail_error().unwrap_err().to_string());
}

#[test]
fn test_ensure() {
    let f = || {
        ensure!(1 + 1 == 2, "This is correct");
        Ok(())
    };
    assert!(f().is_ok());

    let v = 1;
    let f = || {
        ensure!(v + v == 2, "This is correct, v: {}", v);
        Ok(())
    };
    assert!(f().is_ok());

    let f = || {
        ensure!(v + v == 1, "This is not correct, v: {}", v);
        Ok(())
    };
    assert!(f().is_err());

    let f = || {
        ensure!(v + v == 1);
        Ok(())
    };
    assert_eq!(
        f().unwrap_err().to_string(),
        "Condition failed: `v + v == 1`",
    );
}
<|endoftext|>


<commit_before>#![crate_name = "paste"]
#![feature(rustc_private)]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

use std::io::{BufRead, BufReader, Read, stdin, Write};
use std::iter::repeat;
use std::fs::File;
use std::path::Path;

#[path = "../common/util.rs"]
#[macro_use]
mod util;

static NAME: &'static str = "paste";
static VERSION: &'static str = "1.0.0";

pub fn uumain(args: Vec<String>) -> i32 {
    let program = &args[0];

    let opts = [
        getopts::optflag("s", "serial", "paste one file at a time instead of in parallel"),
        getopts::optopt("d", "delimiters", "reuse characters from LIST instead of TABs", "LIST"),
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit")
    ];
    let matches = match getopts::getopts(&args[1..], &opts) {
        Ok(m) => m,
        Err(e) => crash!(1, "{}", e)
    };
    if matches.opt_present("help") {
        println!("{} {}", NAME, VERSION);
        println!("");
        println!("Usage:");
        println!("  {0} [OPTION]... [FILE]...", program);
        println!("");
        print!("{}", getopts::usage("Write lines consisting of the sequentially corresponding lines from each FILE, separated by TABs, to standard output.", &opts));
    } else if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
    } else {
        let serial = matches.opt_present("serial");
        let delimiters = matches.opt_str("delimiters").unwrap_or("\t".to_string());
        paste(matches.free, serial, delimiters);
    }

    0
}

fn paste(filenames: Vec<String>, serial: bool, delimiters: String) {
    let mut files: Vec<BufReader<Box<Read>>> = filenames.into_iter().map(|name|
        BufReader::new(
            if name == "-" {
                Box::new(stdin()) as Box<Read>
            } else {
                let r = crash_if_err!(1, File::open(Path::new(&name)));
                Box::new(r) as Box<Read>
            }
        )
    ).collect();
    let delimiters: Vec<String> = delimiters.chars().map(|x| x.to_string()).collect();
    let mut delim_count = 0;
    if serial {
        for file in files.iter_mut() {
            let mut output = String::new();
            loop {
                let mut line = String::new();
                match file.read_line(&mut line) {
                    Ok(0) => break,
                    Ok(_) => {
                        output.push_str(line.trim_right());
                        output.push_str(&delimiters[delim_count % delimiters.len()]);
                    }
                    Err(e) => crash!(1, "{}", e.to_string())
                }
                delim_count += 1;
            }
            println!("{}", &output[..output.len()-1]);
        }
    } else {
        let mut eof: Vec<bool> = repeat(false).take(files.len()).collect();
        loop {
            let mut output = String::new();
            let mut eof_count = 0;
            for (i, file) in files.iter_mut().enumerate() {
                if eof[i] {
                    eof_count += 1;
                } else {
                    let mut line = String::new();
                    match file.read_line(&mut line) {
                        Ok(0) => {
                            eof[i] = true;
                            eof_count += 1;
                        }
                        Ok(_) => output.push_str(line.trim_right()),
                        Err(e) => crash!(1, "{}", e.to_string())
                    }
                }
                output.push_str(&delimiters[delim_count % delimiters.len()]);
                delim_count += 1;
            }
            if files.len() == eof_count {
                break;
            }
            println!("{}", &output[..output.len()-1]);
            delim_count = 0;
        }
    }
}
<commit_msg>Unescape all special characters in delimiter list.<commit_after>#![crate_name = "paste"]
#![feature(rustc_private)]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

use std::io::{BufRead, BufReader, Read, stdin, Write};
use std::iter::repeat;
use std::fs::File;
use std::path::Path;

#[path = "../common/util.rs"]
#[macro_use]
mod util;

static NAME: &'static str = "paste";
static VERSION: &'static str = "1.0.0";

pub fn uumain(args: Vec<String>) -> i32 {
    let program = &args[0];

    let opts = [
        getopts::optflag("s", "serial", "paste one file at a time instead of in parallel"),
        getopts::optopt("d", "delimiters", "reuse characters from LIST instead of TABs", "LIST"),
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit")
    ];
    let matches = match getopts::getopts(&args[1..], &opts) {
        Ok(m) => m,
        Err(e) => crash!(1, "{}", e)
    };
    if matches.opt_present("help") {
        println!("{} {}", NAME, VERSION);
        println!("");
        println!("Usage:");
        println!("  {0} [OPTION]... [FILE]...", program);
        println!("");
        print!("{}", getopts::usage("Write lines consisting of the sequentially corresponding lines from each FILE, separated by TABs, to standard output.", &opts));
    } else if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
    } else {
        let serial = matches.opt_present("serial");
        let delimiters = matches.opt_str("delimiters").unwrap_or("\t".to_string());
        paste(matches.free, serial, delimiters);
    }

    0
}

fn paste(filenames: Vec<String>, serial: bool, delimiters: String) {
    let mut files: Vec<BufReader<Box<Read>>> = filenames.into_iter().map(|name|
        BufReader::new(
            if name == "-" {
                Box::new(stdin()) as Box<Read>
            } else {
                let r = crash_if_err!(1, File::open(Path::new(&name)));
                Box::new(r) as Box<Read>
            }
        )
    ).collect();

    let delimiters: Vec<String> = unescape(delimiters).chars().map(|x| x.to_string()).collect();
    let mut delim_count = 0;

    if serial {
        for file in files.iter_mut() {
            let mut output = String::new();
            loop {
                let mut line = String::new();
                match file.read_line(&mut line) {
                    Ok(0) => break,
                    Ok(_) => {
                        output.push_str(line.trim_right());
                        output.push_str(&delimiters[delim_count % delimiters.len()]);
                    }
                    Err(e) => crash!(1, "{}", e.to_string())
                }
                delim_count += 1;
            }
            println!("{}", &output[..output.len()-1]);
        }
    } else {
        let mut eof: Vec<bool> = repeat(false).take(files.len()).collect();
        loop {
            let mut output = String::new();
            let mut eof_count = 0;
            for (i, file) in files.iter_mut().enumerate() {
                if eof[i] {
                    eof_count += 1;
                } else {
                    let mut line = String::new();
                    match file.read_line(&mut line) {
                        Ok(0) => {
                            eof[i] = true;
                            eof_count += 1;
                        }
                        Ok(_) => output.push_str(line.trim_right()),
                        Err(e) => crash!(1, "{}", e.to_string())
                    }
                }
                output.push_str(&delimiters[delim_count % delimiters.len()]);
                delim_count += 1;
            }
            if files.len() == eof_count {
                break;
            }
            println!("{}", &output[..output.len()-1]);
            delim_count = 0;
        }
    }
}

// Unescape all special characters
// TODO: this will need work to conform to GNU implementation
fn unescape(s: String) -> String {
    s.replace("\\n", "\n")
     .replace("\\t", "\t")
     .replace("\\\\", "\\")
     .replace("\\", "")
}
<|endoftext|>


<commit_before>
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Serialize, Deserialize)]
pub enum Arch {
  X86,
  X86_64,
  Mips,
  PowerPC,
  PowerPC64,
  Arm,
  AArch64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Serialize, Deserialize)]
pub enum OS {
  Windows,
  MacOS,
  IOS,
  Linux,
  Android,
  FreeBSD,
  DragonFly,
  Bitrig,
  OpenBSD,
  NetBSD,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Serialize, Deserialize)]
pub enum Family {
  Windows,
  Unix,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Serialize, Deserialize)]
pub enum Env {
  Gnu,
  Msvc,
  Musl,
  None,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Serialize, Deserialize)]
pub enum PointerWidth {
  P64,
  P32,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Serialize, Deserialize)]
pub enum Endian {
  Little,
  Big,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Serialize, Deserialize)]
pub struct Target {
  pub arch: Arch,
  pub os: OS,
  pub family: Family,
  pub env: Env,
  pub pointer_width: PointerWidth,
  //pub vendor: Vendor,
  pub endian: Endian,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Serialize, Deserialize)]
pub enum Condition {
  Arch(Arch),
  OS(OS),
  Family(Family),
  Env(Env),
  PointerWidth(PointerWidth),
  //Vendor(Vendor),
  Endian(Endian),
  And(Vec<Condition>),
  Or(Vec<Condition>),
  Not(Box<Condition>),
  True,
  False,
}

// -------------

/// Information required to build the C++ wrapper library
/// on every supported platform.
#[derive(Default, Debug, Clone)]
#[derive(Serialize, Deserialize)]
pub struct CppBuildConfig {
  items: Vec<CppBuildConfigItem>,
}

#[derive(Debug, Clone)]
#[derive(Serialize, Deserialize)]
struct CppBuildConfigItem {
  condition: ::target::Condition,
  data: CppBuildConfigData,
}

/// Platform-specific information
/// required to build the C++ wrapper library.
#[derive(Debug, Default, Clone)]
#[derive(Serialize, Deserialize)]
pub struct CppBuildConfigData {
  linked_libs: Vec<String>,
  linked_frameworks: Vec<String>,
  cpp_compiler_flags: Vec<String>,
}

impl CppBuildConfigData {
  pub fn new() -> CppBuildConfigData {
    CppBuildConfigData::default()
  }

  /// Adds a library for linking. Used as `-l` option to the linker.
  pub fn add_linked_lib<P: Into<String>>(&mut self, lib: P) {
    self.linked_libs.push(lib.into());
  }

  /// Adds a framework for linking (OS X specific). Used as `-f` option to the linker.
  pub fn add_linked_framework<P: Into<String>>(&mut self, lib: P) {
    self.linked_frameworks.push(lib.into());
  }

  /// Adds a command line argument for the C++ compiler.
  pub fn add_cpp_compiler_flag<P: Into<String>>(&mut self, lib: P) {
    self.cpp_compiler_flags.push(lib.into());
  }

  /// Adds multiple flags. See `CppBuildConfigData::add_cpp_compiler_flag`.
  pub fn add_cpp_compiler_flags<Item, Iter>(&mut self, items: Iter)
    where Item: Into<String>,
          Iter: IntoIterator<Item = Item>
  {
    for item in items {
      self.cpp_compiler_flags.push(item.into());
    }
  }

  pub fn linked_libs(&self) -> &[String] {
    &self.linked_libs
  }

  pub fn linked_frameworks(&self) -> &[String] {
    &self.linked_frameworks
  }

  pub fn cpp_compiler_flags(&self) -> &[String] {
    &self.cpp_compiler_flags
  }

  fn add_from(&mut self, other: &CppBuildConfigData) {
    self.linked_libs.append(&mut other.linked_libs.clone());
    self.linked_frameworks.append(&mut other.linked_frameworks.clone());
    self.cpp_compiler_flags.append(&mut other.cpp_compiler_flags.clone());
  }
}

impl CppBuildConfig {
  pub fn new() -> CppBuildConfig {
    CppBuildConfig::default()
  }
  pub fn add(&mut self, condition: ::target::Condition, data: CppBuildConfigData) {
    self.items.push(CppBuildConfigItem {
      condition: condition,
      data: data,
    });
  }
  pub fn eval(&self, target: &::target::Target) -> CppBuildConfigData {
    let mut data = CppBuildConfigData::default();
    for item in &self.items {
      if item.condition.eval(target) {
        data.add_from(&item.data);
      }
    }
    data
  }
}
<commit_msg>add library type to cpp build config<commit_after>use errors::Result;

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Serialize, Deserialize)]
pub enum Arch {
  X86,
  X86_64,
  Mips,
  PowerPC,
  PowerPC64,
  Arm,
  AArch64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Serialize, Deserialize)]
pub enum OS {
  Windows,
  MacOS,
  IOS,
  Linux,
  Android,
  FreeBSD,
  DragonFly,
  Bitrig,
  OpenBSD,
  NetBSD,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Serialize, Deserialize)]
pub enum Family {
  Windows,
  Unix,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Serialize, Deserialize)]
pub enum Env {
  Gnu,
  Msvc,
  Musl,
  None,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Serialize, Deserialize)]
pub enum PointerWidth {
  P64,
  P32,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Serialize, Deserialize)]
pub enum Endian {
  Little,
  Big,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Serialize, Deserialize)]
pub struct Target {
  pub arch: Arch,
  pub os: OS,
  pub family: Family,
  pub env: Env,
  pub pointer_width: PointerWidth,
  //pub vendor: Vendor,
  pub endian: Endian,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Serialize, Deserialize)]
pub enum Condition {
  Arch(Arch),
  OS(OS),
  Family(Family),
  Env(Env),
  PointerWidth(PointerWidth),
  //Vendor(Vendor),
  Endian(Endian),
  And(Vec<Condition>),
  Or(Vec<Condition>),
  Not(Box<Condition>),
  True,
  False,
}

// -------------

/// Information required to build the C++ wrapper library
/// on every supported platform.
#[derive(Default, Debug, Clone)]
#[derive(Serialize, Deserialize)]
pub struct CppBuildConfig {
  items: Vec<CppBuildConfigItem>,
}

#[derive(Debug, Clone)]
#[derive(Serialize, Deserialize)]
struct CppBuildConfigItem {
  condition: ::target::Condition,
  data: CppBuildConfigData,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Serialize, Deserialize)]
pub enum CppLibraryType {
  Shared,
  Static,
}

/// Platform-specific information
/// required to build the C++ wrapper library.
#[derive(Debug, Clone, Default)]
#[derive(Serialize, Deserialize)]
pub struct CppBuildConfigData {
  linked_libs: Vec<String>,
  linked_frameworks: Vec<String>,
  compiler_flags: Vec<String>,
  library_type: Option<CppLibraryType>,
}

impl CppBuildConfigData {
  pub fn new() -> CppBuildConfigData {
    CppBuildConfigData::default()
  }

  /// Adds a library for linking. Used as `-l` option to the linker.
  pub fn add_linked_lib<P: Into<String>>(&mut self, lib: P) {
    self.linked_libs.push(lib.into());
  }

  /// Adds a framework for linking (OS X specific). Used as `-f` option to the linker.
  pub fn add_linked_framework<P: Into<String>>(&mut self, lib: P) {
    self.linked_frameworks.push(lib.into());
  }

  /// Adds a command line argument for the C++ compiler.
  pub fn add_compiler_flag<P: Into<String>>(&mut self, lib: P) {
    self.compiler_flags.push(lib.into());
  }

  /// Adds multiple flags. See `CppBuildConfigData::add_cpp_compiler_flag`.
  pub fn add_compiler_flags<Item, Iter>(&mut self, items: Iter)
    where Item: Into<String>,
          Iter: IntoIterator<Item = Item>
  {
    for item in items {
      self.compiler_flags.push(item.into());
    }
  }

  /// Sets library type. C++ wrapper is static by default.
  /// Shared library can be used to work around MSVC linker's limitations.
  pub fn set_library_type(&mut self, t: CppLibraryType) {
    self.library_type = Some(t);
  }

  pub fn linked_libs(&self) -> &[String] {
    &self.linked_libs
  }

  pub fn linked_frameworks(&self) -> &[String] {
    &self.linked_frameworks
  }

  pub fn compiler_flags(&self) -> &[String] {
    &self.compiler_flags
  }

  pub fn library_type(&self) -> Option<CppLibraryType> {
    self.library_type
  }

  fn add_from(&mut self, other: &CppBuildConfigData) -> Result<()> {
    self.linked_libs.append(&mut other.linked_libs.clone());
    self.linked_frameworks.append(&mut other.linked_frameworks.clone());
    self.compiler_flags.append(&mut other.compiler_flags.clone());
    if self.library_type.is_some() {
      if other.library_type.is_some() && other.library_type != self.library_type {
        return Err("conflicting library types specified".into());
      }
    } else {
      self.library_type = other.library_type;
    }
    Ok(())
  }
}

impl CppBuildConfig {
  pub fn new() -> CppBuildConfig {
    CppBuildConfig::default()
  }
  pub fn add(&mut self, condition: ::target::Condition, data: CppBuildConfigData) {
    self.items.push(CppBuildConfigItem {
      condition: condition,
      data: data,
    });
  }
  pub fn eval(&self, target: &::target::Target) -> CppBuildConfigData {
    let mut data = CppBuildConfigData::default();
    for item in &self.items {
      if item.condition.eval(target) {
        data.add_from(&item.data);
      }
    }
    data
  }
}
<|endoftext|>


<commit_before>// Copyright (C) 2019 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

#[cfg(test)]
mod test;

pub(crate) mod download_abort;
pub(crate) mod info;
pub(crate) mod local_install;
pub(crate) mod probe;
pub(crate) mod remote_install;
/// Used to send `Step` messages to the `Machine` actor.
pub(crate) mod stepper;

use super::{
    DirectDownload, Idle, Metadata, PrepareLocalInstall, Probe, RuntimeSettings, Settings, State,
    StateMachine,
};
use actix::{Actor, Addr, Arbiter, AsyncContext, Context, Handler, Message, ResponseFuture};
use slog_scope::info;

pub(crate) struct Machine {
    state: Option<StateMachine>,
    shared_state: SharedState,
    stepper: stepper::Controller,
}

#[derive(Debug, PartialEq)]
pub(super) struct SharedState {
    pub(super) settings: Settings,
    pub(super) runtime_settings: RuntimeSettings,
    pub(super) firmware: Metadata,
}

impl SharedState {
    pub(super) fn server_address(&self) -> &str {
        self.runtime_settings
            .custom_server_address()
            .unwrap_or(&self.settings.network.server_address)
    }
}

impl Actor for Machine {
    type Context = Context<Self>;

    fn started(&mut self, _: &mut Self::Context) {
        info!("Starting State Machine Actor...");
    }

    fn stopped(&mut self, _: &mut Self::Context) {
        info!("Stopping State Machine Actor...");
    }
}

impl Machine {
    pub(super) fn new(
        state: StateMachine,
        settings: Settings,
        runtime_settings: RuntimeSettings,
        firmware: Metadata,
    ) -> Self {
        Machine {
            state: Some(state),
            shared_state: SharedState { settings, runtime_settings, firmware },
            stepper: stepper::Controller::default(),
        }
    }

    pub(super) fn start(mut self) -> Addr<Self> {
        Machine::start_in_arbiter(&Arbiter::new(), move |ctx| {
            self.stepper.start(ctx.address());
            self
        })
    }
}

#[derive(Message)]
#[rtype(StepTransition)]
struct Step;

pub(crate) enum StepTransition {
    Delayed(std::time::Duration),
    Immediate,
    Never,
}

impl Handler<Step> for Machine {
    type Result = ResponseFuture<StepTransition>;

    fn handle(&mut self, _: Step, _: &mut Context<Self>) -> Self::Result {
        if let Some(machine) = self.state.take() {
            let this: *mut Self = self;

            return Box::pin(async move {
                unsafe {
                    let (state, transition) = machine
                        .move_to_next_state(&mut (*this).shared_state)
                        .await
                        .unwrap_or_else(|e| (StateMachine::from(e), StepTransition::Immediate));
                    (*this).state = Some(state);

                    transition
                }
            });
        }

        unreachable!("Failed to take StateMachine from StateAgent")
    }
}
<commit_msg>updatehub: Handle state machine steps atomically<commit_after>// Copyright (C) 2019 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

#[cfg(test)]
mod test;

pub(crate) mod download_abort;
pub(crate) mod info;
pub(crate) mod local_install;
pub(crate) mod probe;
pub(crate) mod remote_install;
/// Used to send `Step` messages to the `Machine` actor.
pub(crate) mod stepper;

use super::{
    DirectDownload, Idle, Metadata, PrepareLocalInstall, Probe, RuntimeSettings, Settings, State,
    StateMachine,
};
use actix::{
    fut::WrapFuture, Actor, Addr, Arbiter, AsyncContext, AtomicResponse, Context, Handler, Message,
};
use slog_scope::info;

pub(crate) struct Machine {
    state: Option<StateMachine>,
    shared_state: SharedState,
    stepper: stepper::Controller,
}

#[derive(Debug, PartialEq)]
pub(super) struct SharedState {
    pub(super) settings: Settings,
    pub(super) runtime_settings: RuntimeSettings,
    pub(super) firmware: Metadata,
}

impl SharedState {
    pub(super) fn server_address(&self) -> &str {
        self.runtime_settings
            .custom_server_address()
            .unwrap_or(&self.settings.network.server_address)
    }
}

impl Actor for Machine {
    type Context = Context<Self>;

    fn started(&mut self, _: &mut Self::Context) {
        info!("Starting State Machine Actor...");
    }

    fn stopped(&mut self, _: &mut Self::Context) {
        info!("Stopping State Machine Actor...");
    }
}

impl Machine {
    pub(super) fn new(
        state: StateMachine,
        settings: Settings,
        runtime_settings: RuntimeSettings,
        firmware: Metadata,
    ) -> Self {
        Machine {
            state: Some(state),
            shared_state: SharedState { settings, runtime_settings, firmware },
            stepper: stepper::Controller::default(),
        }
    }

    pub(super) fn start(mut self) -> Addr<Self> {
        Machine::start_in_arbiter(&Arbiter::new(), move |ctx| {
            self.stepper.start(ctx.address());
            self
        })
    }
}

#[derive(Message)]
#[rtype(StepTransition)]
struct Step;

pub(crate) enum StepTransition {
    Delayed(std::time::Duration),
    Immediate,
    Never,
}

impl Handler<Step> for Machine {
    type Result = AtomicResponse<Self, StepTransition>;

    fn handle(&mut self, _: Step, _: &mut Context<Self>) -> Self::Result {
        if let Some(machine) = self.state.take() {
            let this: *mut Self = self;

            return AtomicResponse::new(Box::pin(
                async move {
                    let this = unsafe { this.as_mut().unwrap() };
                    let (state, transition) = machine
                        .move_to_next_state(&mut this.shared_state)
                        .await
                        .unwrap_or_else(|e| (StateMachine::from(e), StepTransition::Immediate));
                    this.state = Some(state);

                    transition
                }
                .into_actor(self),
            ));
        }

        unreachable!("Failed to take StateMachine from StateAgent")
    }
}
<|endoftext|>