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

pub static DEFAULT_SIGNAL: usize = 15;

pub struct Signal<'a> {
    pub name: &'a str,
    pub value: usize,
}

/*

Linux Programmer's Manual

 1 HUP      2 INT      3 QUIT     4 ILL      5 TRAP     6 ABRT     7 BUS
 8 FPE      9 KILL    10 USR1    11 SEGV    12 USR2    13 PIPE    14 ALRM
15 TERM    16 STKFLT  17 CHLD    18 CONT    19 STOP    20 TSTP    21 TTIN
22 TTOU    23 URG     24 XCPU    25 XFSZ    26 VTALRM  27 PROF    28 WINCH
29 POLL    30 PWR     31 SYS

*/

#[cfg(target_os = "linux")]
pub static ALL_SIGNALS: [Signal<'static>; 31] = [
    Signal {
        name: "HUP",
        value: 1,
    },
    Signal {
        name: "INT",
        value: 2,
    },
    Signal {
        name: "QUIT",
        value: 3,
    },
    Signal {
        name: "ILL",
        value: 4,
    },
    Signal {
        name: "TRAP",
        value: 5,
    },
    Signal {
        name: "ABRT",
        value: 6,
    },
    Signal {
        name: "BUS",
        value: 7,
    },
    Signal {
        name: "FPE",
        value: 8,
    },
    Signal {
        name: "KILL",
        value: 9,
    },
    Signal {
        name: "USR1",
        value: 10,
    },
    Signal {
        name: "SEGV",
        value: 11,
    },
    Signal {
        name: "USR2",
        value: 12,
    },
    Signal {
        name: "PIPE",
        value: 13,
    },
    Signal {
        name: "ALRM",
        value: 14,
    },
    Signal {
        name: "TERM",
        value: 15,
    },
    Signal {
        name: "STKFLT",
        value: 16,
    },
    Signal {
        name: "CHLD",
        value: 17,
    },
    Signal {
        name: "CONT",
        value: 18,
    },
    Signal {
        name: "STOP",
        value: 19,
    },
    Signal {
        name: "TSTP",
        value: 20,
    },
    Signal {
        name: "TTIN",
        value: 21,
    },
    Signal {
        name: "TTOU",
        value: 22,
    },
    Signal {
        name: "URG",
        value: 23,
    },
    Signal {
        name: "XCPU",
        value: 24,
    },
    Signal {
        name: "XFSZ",
        value: 25,
    },
    Signal {
        name: "VTALRM",
        value: 26,
    },
    Signal {
        name: "PROF",
        value: 27,
    },
    Signal {
        name: "WINCH",
        value: 28,
    },
    Signal {
        name: "POLL",
        value: 29,
    },
    Signal {
        name: "PWR",
        value: 30,
    },
    Signal {
        name: "SYS",
        value: 31,
    },
];

/*

https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/signal.3.html

No    Name         Default Action       Description
1     SIGHUP       terminate process    terminal line hangup
2     SIGINT       terminate process    interrupt program
3     SIGQUIT      create core image    quit program
4     SIGILL       create core image    illegal instruction
5     SIGTRAP      create core image    trace trap
6     SIGABRT      create core image    abort program (formerly SIGIOT)
7     SIGEMT       create core image    emulate instruction executed
8     SIGFPE       create core image    floating-point exception
9     SIGKILL      terminate process    kill program
10    SIGBUS       create core image    bus error
11    SIGSEGV      create core image    segmentation violation
12    SIGSYS       create core image    non-existent system call invoked
13    SIGPIPE      terminate process    write on a pipe with no reader
14    SIGALRM      terminate process    real-time timer expired
15    SIGTERM      terminate process    software termination signal
16    SIGURG       discard signal       urgent condition present on socket
17    SIGSTOP      stop process         stop (cannot be caught or ignored)
18    SIGTSTP      stop process         stop signal generated from keyboard
19    SIGCONT      discard signal       continue after stop
20    SIGCHLD      discard signal       child status has changed
21    SIGTTIN      stop process         background read attempted from control terminal
22    SIGTTOU      stop process         background write attempted to control terminal
23    SIGIO        discard signal       I/O is possible on a descriptor (see fcntl(2))
24    SIGXCPU      terminate process    cpu time limit exceeded (see setrlimit(2))
25    SIGXFSZ      terminate process    file size limit exceeded (see setrlimit(2))
26    SIGVTALRM    terminate process    virtual time alarm (see setitimer(2))
27    SIGPROF      terminate process    profiling timer alarm (see setitimer(2))
28    SIGWINCH     discard signal       Window size change
29    SIGINFO      discard signal       status request from keyboard
30    SIGUSR1      terminate process    User defined signal 1
31    SIGUSR2      terminate process    User defined signal 2

*/

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
pub static ALL_SIGNALS: [Signal<'static>; 31] = [
    Signal {
        name: "HUP",
        value: 1,
    },
    Signal {
        name: "INT",
        value: 2,
    },
    Signal {
        name: "QUIT",
        value: 3,
    },
    Signal {
        name: "ILL",
        value: 4,
    },
    Signal {
        name: "TRAP",
        value: 5,
    },
    Signal {
        name: "ABRT",
        value: 6,
    },
    Signal {
        name: "EMT",
        value: 7,
    },
    Signal {
        name: "FPE",
        value: 8,
    },
    Signal {
        name: "KILL",
        value: 9,
    },
    Signal {
        name: "BUS",
        value: 10,
    },
    Signal {
        name: "SEGV",
        value: 11,
    },
    Signal {
        name: "SYS",
        value: 12,
    },
    Signal {
        name: "PIPE",
        value: 13,
    },
    Signal {
        name: "ALRM",
        value: 14,
    },
    Signal {
        name: "TERM",
        value: 15,
    },
    Signal {
        name: "URG",
        value: 16,
    },
    Signal {
        name: "STOP",
        value: 17,
    },
    Signal {
        name: "TSTP",
        value: 18,
    },
    Signal {
        name: "CONT",
        value: 19,
    },
    Signal {
        name: "CHLD",
        value: 20,
    },
    Signal {
        name: "TTIN",
        value: 21,
    },
    Signal {
        name: "TTOU",
        value: 22,
    },
    Signal {
        name: "IO",
        value: 23,
    },
    Signal {
        name: "XCPU",
        value: 24,
    },
    Signal {
        name: "XFSZ",
        value: 25,
    },
    Signal {
        name: "VTALRM",
        value: 26,
    },
    Signal {
        name: "PROF",
        value: 27,
    },
    Signal {
        name: "WINCH",
        value: 28,
    },
    Signal {
        name: "INFO",
        value: 29,
    },
    Signal {
        name: "USR1",
        value: 30,
    },
    Signal {
        name: "USR2",
        value: 31,
    },
];

pub fn signal_by_name_or_value(signal_name_or_value: &str) -> Option<usize> {
    if let Ok(value) = signal_name_or_value.parse() {
        if is_signal(value) {
            return Some(value);
        } else {
            return None;
        }
    }
    let signal_name = if signal_name_or_value.starts_with("SIG") {
        &signal_name_or_value[3..]
    } else {
        &signal_name_or_value[..]
    };

    ALL_SIGNALS
        .iter()
        .find(|s| s.name == signal_name)
        .map(|s| s.value)
}

#[inline(always)]
pub fn is_signal(num: usize) -> bool {
    // Named signals start at 1
    num <= ALL_SIGNALS.len()
}

#[test]
fn signals_all_contiguous() {
    for (i, signal) in ALL_SIGNALS.iter().enumerate() {
        assert_eq!(signal.value, i + 1);
    }
}

#[test]
fn signals_all_are_signal() {
    for signal in &ALL_SIGNALS {
        assert!(is_signal(signal.value));
    }
}

#[test]
fn signal_by_value() {
    assert_eq!(signal_by_name_or_value("0"), Some(0));
    for signal in &ALL_SIGNALS {
        assert_eq!(
            signal_by_name_or_value(&signal.value.to_string()),
            Some(signal.value)
        );
    }
}

#[test]
fn signal_by_short_name() {
    for signal in &ALL_SIGNALS {
        assert_eq!(signal_by_name_or_value(signal.name), Some(signal.value));
    }
}

#[test]
fn signal_by_long_name() {
    for signal in &ALL_SIGNALS {
        assert_eq!(
            signal_by_name_or_value(&format!("SIG{}", signal.name)),
            Some(signal.value)
        );
    }
}
<commit_msg>Replace starts_with and slice with trim_left_matches<commit_after>/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Maciej Dziardziel <fiedzia@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE file
 * that was distributed with this source code.
 */

pub static DEFAULT_SIGNAL: usize = 15;

pub struct Signal<'a> {
    pub name: &'a str,
    pub value: usize,
}

/*

Linux Programmer's Manual

 1 HUP      2 INT      3 QUIT     4 ILL      5 TRAP     6 ABRT     7 BUS
 8 FPE      9 KILL    10 USR1    11 SEGV    12 USR2    13 PIPE    14 ALRM
15 TERM    16 STKFLT  17 CHLD    18 CONT    19 STOP    20 TSTP    21 TTIN
22 TTOU    23 URG     24 XCPU    25 XFSZ    26 VTALRM  27 PROF    28 WINCH
29 POLL    30 PWR     31 SYS

*/

#[cfg(target_os = "linux")]
pub static ALL_SIGNALS: [Signal<'static>; 31] = [
    Signal {
        name: "HUP",
        value: 1,
    },
    Signal {
        name: "INT",
        value: 2,
    },
    Signal {
        name: "QUIT",
        value: 3,
    },
    Signal {
        name: "ILL",
        value: 4,
    },
    Signal {
        name: "TRAP",
        value: 5,
    },
    Signal {
        name: "ABRT",
        value: 6,
    },
    Signal {
        name: "BUS",
        value: 7,
    },
    Signal {
        name: "FPE",
        value: 8,
    },
    Signal {
        name: "KILL",
        value: 9,
    },
    Signal {
        name: "USR1",
        value: 10,
    },
    Signal {
        name: "SEGV",
        value: 11,
    },
    Signal {
        name: "USR2",
        value: 12,
    },
    Signal {
        name: "PIPE",
        value: 13,
    },
    Signal {
        name: "ALRM",
        value: 14,
    },
    Signal {
        name: "TERM",
        value: 15,
    },
    Signal {
        name: "STKFLT",
        value: 16,
    },
    Signal {
        name: "CHLD",
        value: 17,
    },
    Signal {
        name: "CONT",
        value: 18,
    },
    Signal {
        name: "STOP",
        value: 19,
    },
    Signal {
        name: "TSTP",
        value: 20,
    },
    Signal {
        name: "TTIN",
        value: 21,
    },
    Signal {
        name: "TTOU",
        value: 22,
    },
    Signal {
        name: "URG",
        value: 23,
    },
    Signal {
        name: "XCPU",
        value: 24,
    },
    Signal {
        name: "XFSZ",
        value: 25,
    },
    Signal {
        name: "VTALRM",
        value: 26,
    },
    Signal {
        name: "PROF",
        value: 27,
    },
    Signal {
        name: "WINCH",
        value: 28,
    },
    Signal {
        name: "POLL",
        value: 29,
    },
    Signal {
        name: "PWR",
        value: 30,
    },
    Signal {
        name: "SYS",
        value: 31,
    },
];

/*

https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/signal.3.html

No    Name         Default Action       Description
1     SIGHUP       terminate process    terminal line hangup
2     SIGINT       terminate process    interrupt program
3     SIGQUIT      create core image    quit program
4     SIGILL       create core image    illegal instruction
5     SIGTRAP      create core image    trace trap
6     SIGABRT      create core image    abort program (formerly SIGIOT)
7     SIGEMT       create core image    emulate instruction executed
8     SIGFPE       create core image    floating-point exception
9     SIGKILL      terminate process    kill program
10    SIGBUS       create core image    bus error
11    SIGSEGV      create core image    segmentation violation
12    SIGSYS       create core image    non-existent system call invoked
13    SIGPIPE      terminate process    write on a pipe with no reader
14    SIGALRM      terminate process    real-time timer expired
15    SIGTERM      terminate process    software termination signal
16    SIGURG       discard signal       urgent condition present on socket
17    SIGSTOP      stop process         stop (cannot be caught or ignored)
18    SIGTSTP      stop process         stop signal generated from keyboard
19    SIGCONT      discard signal       continue after stop
20    SIGCHLD      discard signal       child status has changed
21    SIGTTIN      stop process         background read attempted from control terminal
22    SIGTTOU      stop process         background write attempted to control terminal
23    SIGIO        discard signal       I/O is possible on a descriptor (see fcntl(2))
24    SIGXCPU      terminate process    cpu time limit exceeded (see setrlimit(2))
25    SIGXFSZ      terminate process    file size limit exceeded (see setrlimit(2))
26    SIGVTALRM    terminate process    virtual time alarm (see setitimer(2))
27    SIGPROF      terminate process    profiling timer alarm (see setitimer(2))
28    SIGWINCH     discard signal       Window size change
29    SIGINFO      discard signal       status request from keyboard
30    SIGUSR1      terminate process    User defined signal 1
31    SIGUSR2      terminate process    User defined signal 2

*/

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
pub static ALL_SIGNALS: [Signal<'static>; 31] = [
    Signal {
        name: "HUP",
        value: 1,
    },
    Signal {
        name: "INT",
        value: 2,
    },
    Signal {
        name: "QUIT",
        value: 3,
    },
    Signal {
        name: "ILL",
        value: 4,
    },
    Signal {
        name: "TRAP",
        value: 5,
    },
    Signal {
        name: "ABRT",
        value: 6,
    },
    Signal {
        name: "EMT",
        value: 7,
    },
    Signal {
        name: "FPE",
        value: 8,
    },
    Signal {
        name: "KILL",
        value: 9,
    },
    Signal {
        name: "BUS",
        value: 10,
    },
    Signal {
        name: "SEGV",
        value: 11,
    },
    Signal {
        name: "SYS",
        value: 12,
    },
    Signal {
        name: "PIPE",
        value: 13,
    },
    Signal {
        name: "ALRM",
        value: 14,
    },
    Signal {
        name: "TERM",
        value: 15,
    },
    Signal {
        name: "URG",
        value: 16,
    },
    Signal {
        name: "STOP",
        value: 17,
    },
    Signal {
        name: "TSTP",
        value: 18,
    },
    Signal {
        name: "CONT",
        value: 19,
    },
    Signal {
        name: "CHLD",
        value: 20,
    },
    Signal {
        name: "TTIN",
        value: 21,
    },
    Signal {
        name: "TTOU",
        value: 22,
    },
    Signal {
        name: "IO",
        value: 23,
    },
    Signal {
        name: "XCPU",
        value: 24,
    },
    Signal {
        name: "XFSZ",
        value: 25,
    },
    Signal {
        name: "VTALRM",
        value: 26,
    },
    Signal {
        name: "PROF",
        value: 27,
    },
    Signal {
        name: "WINCH",
        value: 28,
    },
    Signal {
        name: "INFO",
        value: 29,
    },
    Signal {
        name: "USR1",
        value: 30,
    },
    Signal {
        name: "USR2",
        value: 31,
    },
];

pub fn signal_by_name_or_value(signal_name_or_value: &str) -> Option<usize> {
    if let Ok(value) = signal_name_or_value.parse() {
        if is_signal(value) {
            return Some(value);
        } else {
            return None;
        }
    }
    let signal_name = signal_name_or_value.trim_left_matches("SIG");

    ALL_SIGNALS
        .iter()
        .find(|s| s.name == signal_name)
        .map(|s| s.value)
}

#[inline(always)]
pub fn is_signal(num: usize) -> bool {
    // Named signals start at 1
    num <= ALL_SIGNALS.len()
}

#[test]
fn signals_all_contiguous() {
    for (i, signal) in ALL_SIGNALS.iter().enumerate() {
        assert_eq!(signal.value, i + 1);
    }
}

#[test]
fn signals_all_are_signal() {
    for signal in &ALL_SIGNALS {
        assert!(is_signal(signal.value));
    }
}

#[test]
fn signal_by_value() {
    assert_eq!(signal_by_name_or_value("0"), Some(0));
    for signal in &ALL_SIGNALS {
        assert_eq!(
            signal_by_name_or_value(&signal.value.to_string()),
            Some(signal.value)
        );
    }
}

#[test]
fn signal_by_short_name() {
    for signal in &ALL_SIGNALS {
        assert_eq!(signal_by_name_or_value(signal.name), Some(signal.value));
    }
}

#[test]
fn signal_by_long_name() {
    for signal in &ALL_SIGNALS {
        assert_eq!(
            signal_by_name_or_value(&format!("SIG{}", signal.name)),
            Some(signal.value)
        );
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::mem;

use sha1::Sha1;

use wasm_context::{
    FnTy,
    FunctionKind,
    FunctionIndex,
    TypeIndex,
    Import,
    Export,
    Context,
    Opcode
};

fn hash( string: &str ) -> String {
    let mut hasher = Sha1::new();
    hasher.update( string.as_bytes() );
    format!( "{}", hasher.digest() )
}

pub struct JsSnippet {
    pub name: String,
    pub code: String,
    pub ty: FnTy
}

impl JsSnippet {
    pub fn arg_count( &self ) -> usize {
        self.ty.params.len()
    }
}

struct Snippet {
    name: String,
    code: String,
    ty: FnTy,
    function_index: FunctionIndex
}

pub fn process_and_extract( ctx: &mut Context ) -> Vec< JsSnippet > {
    let mut shim_map: HashMap< FunctionIndex, TypeIndex > = HashMap::new();
    let mut snippet_offset_to_type_index = HashMap::new();
    let mut snippet_index_by_offset = HashMap::new();
    let mut snippet_index_by_hash: HashMap< String, usize > = HashMap::new();
    let mut snippets = Vec::new();

    for (&function_index, function) in &ctx.functions {
        if let &FunctionKind::Import { type_index, ref import, .. } = function {
            if import.module == "env" && import.field.starts_with( "__js_" ) {
                shim_map.insert( function_index, type_index );
            }
        }
    }

    for (_, function) in &ctx.functions {
        if let &FunctionKind::Definition { ref opcodes, .. } = function {
            for (index, opcode) in opcodes.iter().enumerate() {
                match opcode {
                    &Opcode::Call( function_index ) => {
                        if let Some( &type_index ) = shim_map.get( &function_index ) {
                            match opcodes[ index - 1 ] {
                                Opcode::I32Const( offset ) => {
                                    if let Some( previous_ty ) = snippet_offset_to_type_index.get( &offset ).cloned() {
                                        if type_index != previous_ty {
                                            panic!( "internal error: same snippet of JS (by offset) is used with two different shims; please report this!" );
                                        }
                                    }

                                    snippet_offset_to_type_index.insert( offset, type_index );
                                },
                                _ => panic!( "internal error: unexpected way of calling JS shims; please report this!" )
                            }
                        }
                    },
                    _ => {}
                }
            }
        }
    }

    let mut data_entries = Vec::new();
    mem::swap( &mut data_entries, &mut ctx.data );

    fn add_js_snippet( ctx: &mut Context, value_slice: &[u8], snippet_index_by_hash: &mut HashMap< String, usize >, snippet_index_by_offset: &mut HashMap< i32, usize >, snippets: &mut Vec< Snippet >, offset: i32, type_index: u32 ) {
        let code = match String::from_utf8( value_slice.to_owned() ) {
            Ok( code ) => code,
            Err( _ ) => {
                panic!( "You have invalid UTF-8 in one of your `js!` snippets! (offset = {}, length = {})", offset, value_slice.len() );
            }
        };

        let code_hash = hash( &code );

        let shim_ty = ctx.fn_ty_by_index( type_index ).unwrap();
        let ty = FnTy {
            params: shim_ty.params.iter().cloned().take( shim_ty.params.len() - 1 ).collect(),
            return_type: shim_ty.return_type.clone()
        };

        if let Some( &snippet_index ) = snippet_index_by_hash.get( &code_hash ) {
            let snippet: &Snippet = &snippets[ snippet_index ];
            if snippet.ty != ty {
                panic!( "internal error: same snippet of JS (by value) is used with two different shims; please report this!" );
            }

            snippet_index_by_offset.insert( offset, snippet_index );
        } else {
            let snippet = Snippet {
                name: format!( "__extjs_{}", code_hash ),
                code,
                ty,
                function_index: 0xFFFFFFFF
            };

            snippet_index_by_offset.insert( offset, snippets.len() );
            snippet_index_by_hash.insert( code_hash, snippets.len() );
            snippets.push( snippet );
        }
    }

    // For older Rust nightlies.
    data_entries.retain( |data| {
        if data.offset.len() != 2 {
            return true;
        }

        let (offset, type_index) = match data.constant_offset() {
            Some( offset ) => {
                if let Some( &type_index ) = snippet_offset_to_type_index.get( &offset ) {
                    (offset, type_index)
                } else {
                    return true;
                }
            },
            _ => return true
        };

        let slice = data.value.as_slice();
        let slice = &slice[ 0..slice.iter().position( |&byte| byte == 0 ).unwrap_or( slice.len() ) ];
        if slice.len() + 1 < data.value.len() {
            return true;
        }

        add_js_snippet( ctx, slice, &mut snippet_index_by_hash, &mut snippet_index_by_offset, &mut snippets, offset, type_index );
        return false;
    });

    // For newer Rust nightlies.
    for (offset, type_index) in snippet_offset_to_type_index {
        if snippet_index_by_offset.contains_key( &offset ) {
            continue; // Already done.
        }

        let (data, data_offset) = data_entries.iter()
            .filter_map( |data| {
                if let Some( offset ) = data.constant_offset() {
                    Some( (data, offset) )
                } else {
                    None
                }
            })
            .find( |&(data, data_offset)| offset >= data_offset && offset < (data_offset + data.value.len() as i32) )
            .expect( "js! snippet not found in data section" );

        let relative_offset = offset - data_offset;
        let slice = &data.value[ relative_offset as usize.. ];
        let slice = &slice[ 0..slice.iter().position( |&byte| byte == 0 ).unwrap_or( slice.len() ) ];

        // TODO: Purge this with the help of the new "linking" WASM section?
        add_js_snippet( ctx, slice, &mut snippet_index_by_hash, &mut snippet_index_by_offset, &mut snippets, offset, type_index );
    }

    ctx.data = data_entries;

    for snippet in &mut snippets {
        let type_index = ctx.get_or_add_fn_type( snippet.ty.clone() );
        let function_index = ctx.add_function( FunctionKind::Import {
            export: Export::none(),
            import: Import { module: "env".to_owned(), field: snippet.name.clone() },
            name: Some( snippet.name.clone() ),
            type_index
        });

        snippet.function_index = function_index;
    }

    for function_index in shim_map.keys() {
        ctx.functions.remove( function_index );
    }

    ctx.patch_code( |opcodes| {
        let should_process = opcodes.iter().any( |opcode| {
            match opcode {
                &Opcode::Call( function_index ) => shim_map.contains_key( &function_index ),
                _ => false
            }
        });

        if !should_process {
            return;
        }

        let mut new_opcodes = Vec::with_capacity( opcodes.len() );
        let mut old_opcodes = Vec::new();
        mem::swap( opcodes, &mut old_opcodes );

        for opcode in old_opcodes {
            match opcode {
                Opcode::Call( function_index ) if shim_map.contains_key( &function_index ) => {
                    // Pop the last argument which was a pointer to the code.
                    let offset = match new_opcodes.pop().unwrap() {
                        Opcode::I32Const( offset ) => offset,
                        _ => panic!()
                    };
                    let &snippet_index = snippet_index_by_offset.get( &offset ).unwrap();
                    let snippet = &snippets[ snippet_index ];
                    new_opcodes.push( Opcode::Call( snippet.function_index ) );
                }
                opcode => {
                    new_opcodes.push( opcode );
                }
            }
        }

        *opcodes = new_opcodes;
    });

    snippets.into_iter().map( |snippet| {
        JsSnippet {
            name: snippet.name,
            code: snippet.code,
            ty: snippet.ty
        }
    }).collect()
}
<commit_msg>Improve the panic message when we detect duplicate JS snippets<commit_after>use std::collections::HashMap;
use std::mem;

use sha1::Sha1;

use wasm_context::{
    FnTy,
    FunctionKind,
    FunctionIndex,
    TypeIndex,
    Import,
    Export,
    Context,
    Opcode
};

fn hash( string: &str ) -> String {
    let mut hasher = Sha1::new();
    hasher.update( string.as_bytes() );
    format!( "{}", hasher.digest() )
}

pub struct JsSnippet {
    pub name: String,
    pub code: String,
    pub ty: FnTy
}

impl JsSnippet {
    pub fn arg_count( &self ) -> usize {
        self.ty.params.len()
    }
}

struct Snippet {
    name: String,
    code: String,
    ty: FnTy,
    function_index: FunctionIndex,
    offset: i32
}

pub fn process_and_extract( ctx: &mut Context ) -> Vec< JsSnippet > {
    let mut shim_map: HashMap< FunctionIndex, TypeIndex > = HashMap::new();
    let mut snippet_offset_to_type_index = HashMap::new();
    let mut snippet_index_by_offset = HashMap::new();
    let mut snippet_index_by_hash: HashMap< String, usize > = HashMap::new();
    let mut snippets = Vec::new();

    for (&function_index, function) in &ctx.functions {
        if let &FunctionKind::Import { type_index, ref import, .. } = function {
            if import.module == "env" && import.field.starts_with( "__js_" ) {
                shim_map.insert( function_index, type_index );
            }
        }
    }

    for (_, function) in &ctx.functions {
        if let &FunctionKind::Definition { ref opcodes, .. } = function {
            for (index, opcode) in opcodes.iter().enumerate() {
                match opcode {
                    &Opcode::Call( function_index ) => {
                        if let Some( &type_index ) = shim_map.get( &function_index ) {
                            match opcodes[ index - 1 ] {
                                Opcode::I32Const( offset ) => {
                                    if let Some( previous_ty ) = snippet_offset_to_type_index.get( &offset ).cloned() {
                                        if type_index != previous_ty {
                                            panic!( "internal error: same snippet of JS (by offset) is used with two different shims; please report this!" );
                                        }
                                    }

                                    snippet_offset_to_type_index.insert( offset, type_index );
                                },
                                _ => panic!( "internal error: unexpected way of calling JS shims; please report this!" )
                            }
                        }
                    },
                    _ => {}
                }
            }
        }
    }

    let mut data_entries = Vec::new();
    mem::swap( &mut data_entries, &mut ctx.data );

    fn add_js_snippet( ctx: &mut Context, value_slice: &[u8], snippet_index_by_hash: &mut HashMap< String, usize >, snippet_index_by_offset: &mut HashMap< i32, usize >, snippets: &mut Vec< Snippet >, offset: i32, type_index: u32 ) {
        let code = match String::from_utf8( value_slice.to_owned() ) {
            Ok( code ) => code,
            Err( _ ) => {
                panic!( "You have invalid UTF-8 in one of your `js!` snippets! (offset = {}, length = {})", offset, value_slice.len() );
            }
        };

        let code_hash = hash( &code );

        let shim_ty = ctx.fn_ty_by_index( type_index ).unwrap();
        let ty = FnTy {
            params: shim_ty.params.iter().cloned().take( shim_ty.params.len() - 1 ).collect(),
            return_type: shim_ty.return_type.clone()
        };

        if let Some( &snippet_index ) = snippet_index_by_hash.get( &code_hash ) {
            let snippet: &Snippet = &snippets[ snippet_index ];
            if snippet.ty != ty {
                panic!(
                    "internal error: same snippet of JS (by value) is used with two different shims; please report this!\nfn 1: {:?}\nfn 2: {:?}\nhash: {}\noffset 1: {}\noffset 2: {}\nsnippet:\n\"{}\"",
                    ty,
                    snippet.ty,
                    code_hash,
                    snippet.offset,
                    offset,
                    snippet.code
                );
            }

            snippet_index_by_offset.insert( offset, snippet_index );
        } else {
            let snippet = Snippet {
                name: format!( "__extjs_{}", code_hash ),
                code,
                ty,
                function_index: 0xFFFFFFFF,
                offset
            };

            snippet_index_by_offset.insert( offset, snippets.len() );
            snippet_index_by_hash.insert( code_hash, snippets.len() );
            snippets.push( snippet );
        }
    }

    // For older Rust nightlies.
    data_entries.retain( |data| {
        if data.offset.len() != 2 {
            return true;
        }

        let (offset, type_index) = match data.constant_offset() {
            Some( offset ) => {
                if let Some( &type_index ) = snippet_offset_to_type_index.get( &offset ) {
                    (offset, type_index)
                } else {
                    return true;
                }
            },
            _ => return true
        };

        let slice = data.value.as_slice();
        let slice = &slice[ 0..slice.iter().position( |&byte| byte == 0 ).unwrap_or( slice.len() ) ];
        if slice.len() + 1 < data.value.len() {
            return true;
        }

        add_js_snippet( ctx, slice, &mut snippet_index_by_hash, &mut snippet_index_by_offset, &mut snippets, offset, type_index );
        return false;
    });

    // For newer Rust nightlies.
    for (offset, type_index) in snippet_offset_to_type_index {
        if snippet_index_by_offset.contains_key( &offset ) {
            continue; // Already done.
        }

        let (data, data_offset) = data_entries.iter()
            .filter_map( |data| {
                if let Some( offset ) = data.constant_offset() {
                    Some( (data, offset) )
                } else {
                    None
                }
            })
            .find( |&(data, data_offset)| offset >= data_offset && offset < (data_offset + data.value.len() as i32) )
            .expect( "js! snippet not found in data section" );

        let relative_offset = offset - data_offset;
        let slice = &data.value[ relative_offset as usize.. ];
        let slice = &slice[ 0..slice.iter().position( |&byte| byte == 0 ).unwrap_or( slice.len() ) ];

        // TODO: Purge this with the help of the new "linking" WASM section?
        add_js_snippet( ctx, slice, &mut snippet_index_by_hash, &mut snippet_index_by_offset, &mut snippets, offset, type_index );
    }

    ctx.data = data_entries;

    for snippet in &mut snippets {
        let type_index = ctx.get_or_add_fn_type( snippet.ty.clone() );
        let function_index = ctx.add_function( FunctionKind::Import {
            export: Export::none(),
            import: Import { module: "env".to_owned(), field: snippet.name.clone() },
            name: Some( snippet.name.clone() ),
            type_index
        });

        snippet.function_index = function_index;
    }

    for function_index in shim_map.keys() {
        ctx.functions.remove( function_index );
    }

    ctx.patch_code( |opcodes| {
        let should_process = opcodes.iter().any( |opcode| {
            match opcode {
                &Opcode::Call( function_index ) => shim_map.contains_key( &function_index ),
                _ => false
            }
        });

        if !should_process {
            return;
        }

        let mut new_opcodes = Vec::with_capacity( opcodes.len() );
        let mut old_opcodes = Vec::new();
        mem::swap( opcodes, &mut old_opcodes );

        for opcode in old_opcodes {
            match opcode {
                Opcode::Call( function_index ) if shim_map.contains_key( &function_index ) => {
                    // Pop the last argument which was a pointer to the code.
                    let offset = match new_opcodes.pop().unwrap() {
                        Opcode::I32Const( offset ) => offset,
                        _ => panic!()
                    };
                    let &snippet_index = snippet_index_by_offset.get( &offset ).unwrap();
                    let snippet = &snippets[ snippet_index ];
                    new_opcodes.push( Opcode::Call( snippet.function_index ) );
                }
                opcode => {
                    new_opcodes.push( opcode );
                }
            }
        }

        *opcodes = new_opcodes;
    });

    snippets.into_iter().map( |snippet| {
        JsSnippet {
            name: snippet.name,
            code: snippet.code,
            ty: snippet.ty
        }
    }).collect()
}
<|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/. */

use dom::bindings::codegen::BindingDeclarations::HTMLEmbedElementBinding;
use dom::bindings::codegen::InheritTypes::HTMLEmbedElementDerived;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::error::ErrorResult;
use dom::document::Document;
use dom::element::HTMLEmbedElementTypeId;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId};
use servo_util::str::DOMString;

#[deriving(Encodable)]
pub struct HTMLEmbedElement {
    pub htmlelement: HTMLElement
}

impl HTMLEmbedElementDerived for EventTarget {
    fn is_htmlembedelement(&self) -> bool {
        self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLEmbedElementTypeId))
    }
}

impl HTMLEmbedElement {
    pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLEmbedElement {
        HTMLEmbedElement {
            htmlelement: HTMLElement::new_inherited(HTMLEmbedElementTypeId, localName, document)
        }
    }

    pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLEmbedElement> {
        let element = HTMLEmbedElement::new_inherited(localName, document);
        Node::reflect_node(box element, document, HTMLEmbedElementBinding::Wrap)
    }
}

pub trait HTMLEmbedElementMethods {
    fn Src(&self) -> DOMString;
    fn SetSrc(&mut self, _src: DOMString) -> ErrorResult;
    fn Type(&self) -> DOMString;
    fn SetType(&mut self, _type: DOMString) -> ErrorResult;
    fn Width(&self) -> DOMString;
    fn SetWidth(&mut self, _width: DOMString) -> ErrorResult;
    fn Height(&self) -> DOMString;
    fn SetHeight(&mut self, _height: DOMString) -> ErrorResult;
    fn Align(&self) -> DOMString;
    fn SetAlign(&mut self, _type: DOMString) -> ErrorResult;
    fn Name(&self) -> DOMString;
    fn SetName(&mut self, _type: DOMString) -> ErrorResult;
    fn GetSVGDocument(&self) -> Option<Temporary<Document>>;
}

impl<'a> HTMLEmbedElementMethods for JSRef<'a, HTMLEmbedElement> {
    fn Src(&self) -> DOMString {
        "".to_owned()
    }

    fn SetSrc(&mut self, _src: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Type(&self) -> DOMString {
        "".to_owned()
    }

    fn SetType(&mut self, _type: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Width(&self) -> DOMString {
        "".to_owned()
    }

    fn SetWidth(&mut self, _width: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Height(&self) -> DOMString {
        "".to_owned()
    }

    fn SetHeight(&mut self, _height: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Align(&self) -> DOMString {
        "".to_owned()
    }

    fn SetAlign(&mut self, _type: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Name(&self) -> DOMString {
        "".to_owned()
    }

    fn SetName(&mut self, _type: DOMString) -> ErrorResult {
        Ok(())
    }

    fn GetSVGDocument(&self) -> Option<Temporary<Document>> {
        None
    }
}
<commit_msg>Remove needless '&mut self' from HTMLEmbedElementMethods.<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/. */

use dom::bindings::codegen::BindingDeclarations::HTMLEmbedElementBinding;
use dom::bindings::codegen::InheritTypes::HTMLEmbedElementDerived;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::error::ErrorResult;
use dom::document::Document;
use dom::element::HTMLEmbedElementTypeId;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId};
use servo_util::str::DOMString;

#[deriving(Encodable)]
pub struct HTMLEmbedElement {
    pub htmlelement: HTMLElement
}

impl HTMLEmbedElementDerived for EventTarget {
    fn is_htmlembedelement(&self) -> bool {
        self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLEmbedElementTypeId))
    }
}

impl HTMLEmbedElement {
    pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLEmbedElement {
        HTMLEmbedElement {
            htmlelement: HTMLElement::new_inherited(HTMLEmbedElementTypeId, localName, document)
        }
    }

    pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLEmbedElement> {
        let element = HTMLEmbedElement::new_inherited(localName, document);
        Node::reflect_node(box element, document, HTMLEmbedElementBinding::Wrap)
    }
}

pub trait HTMLEmbedElementMethods {
    fn Src(&self) -> DOMString;
    fn SetSrc(&self, _src: DOMString) -> ErrorResult;
    fn Type(&self) -> DOMString;
    fn SetType(&self, _type: DOMString) -> ErrorResult;
    fn Width(&self) -> DOMString;
    fn SetWidth(&self, _width: DOMString) -> ErrorResult;
    fn Height(&self) -> DOMString;
    fn SetHeight(&self, _height: DOMString) -> ErrorResult;
    fn Align(&self) -> DOMString;
    fn SetAlign(&self, _type: DOMString) -> ErrorResult;
    fn Name(&self) -> DOMString;
    fn SetName(&self, _type: DOMString) -> ErrorResult;
    fn GetSVGDocument(&self) -> Option<Temporary<Document>>;
}

impl<'a> HTMLEmbedElementMethods for JSRef<'a, HTMLEmbedElement> {
    fn Src(&self) -> DOMString {
        "".to_owned()
    }

    fn SetSrc(&self, _src: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Type(&self) -> DOMString {
        "".to_owned()
    }

    fn SetType(&self, _type: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Width(&self) -> DOMString {
        "".to_owned()
    }

    fn SetWidth(&self, _width: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Height(&self) -> DOMString {
        "".to_owned()
    }

    fn SetHeight(&self, _height: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Align(&self) -> DOMString {
        "".to_owned()
    }

    fn SetAlign(&self, _type: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Name(&self) -> DOMString {
        "".to_owned()
    }

    fn SetName(&self, _type: DOMString) -> ErrorResult {
        Ok(())
    }

    fn GetSVGDocument(&self) -> Option<Temporary<Document>> {
        None
    }
}
<|endoftext|>


<commit_before>use shaders::Position;
use specs;
use zombie::ZombieDrawable;

#[derive(Debug, Clone)]
pub struct Zombies {
  pub zombies: Vec<ZombieDrawable>,
}

impl Zombies {
  pub fn new() -> Zombies {
    Zombies {
      zombies: vec![
        ZombieDrawable::new(Position::new([200.0, 10.0])),
        ZombieDrawable::new(Position::new([-200.0, 10.0])),
        ZombieDrawable::new(Position::new([10.0, 200.0])),
        ZombieDrawable::new(Position::new([10.0, -200.0])),

        ZombieDrawable::new(Position::new([500.0, 40.0])),
        ZombieDrawable::new(Position::new([-500.0, 40.0])),
        ZombieDrawable::new(Position::new([40.0, 500.0])),
        ZombieDrawable::new(Position::new([40.0, -500.0])),

        ZombieDrawable::new(Position::new([700.0, 60.0])),
        ZombieDrawable::new(Position::new([-700.0, 60.0])),
        ZombieDrawable::new(Position::new([60.0, 700.0])),
        ZombieDrawable::new(Position::new([60.0, -700.0])),

        ZombieDrawable::new(Position::new([800.0, 160.0])),
        ZombieDrawable::new(Position::new([-800.0, 160.0])),
        ZombieDrawable::new(Position::new([160.0, 800.0])),
        ZombieDrawable::new(Position::new([160.0, -800.0]))
      ]
    }
  }
}

impl specs::prelude::Component for Zombies {
  type Storage = specs::storage::VecStorage<Zombies>;
}
<commit_msg>Update initial zombie positions<commit_after>use shaders::Position;
use specs;
use zombie::ZombieDrawable;

#[derive(Debug, Clone)]
pub struct Zombies {
  pub zombies: Vec<ZombieDrawable>,
}

impl Zombies {
  pub fn new() -> Zombies {
    Zombies {
      zombies: vec![
        ZombieDrawable::new(Position::new([200.0, 10.0])),
        ZombieDrawable::new(Position::new([-200.0, 10.0])),
        ZombieDrawable::new(Position::new([10.0, 200.0])),
        ZombieDrawable::new(Position::new([10.0, -200.0])),

        ZombieDrawable::new(Position::new([500.0, 40.0])),
        ZombieDrawable::new(Position::new([-500.0, 40.0])),
        ZombieDrawable::new(Position::new([40.0, 500.0])),
        ZombieDrawable::new(Position::new([40.0, -500.0])),

        ZombieDrawable::new(Position::new([700.0, 60.0])),
        ZombieDrawable::new(Position::new([-900.0, 60.0])),
        ZombieDrawable::new(Position::new([60.0, 700.0])),
        ZombieDrawable::new(Position::new([60.0, -700.0])),

        ZombieDrawable::new(Position::new([800.0, 160.0])),
        ZombieDrawable::new(Position::new([-1000.0, 160.0])),
        ZombieDrawable::new(Position::new([160.0, 800.0])),
        ZombieDrawable::new(Position::new([160.0, -800.0]))
      ]
    }
  }
}

impl specs::prelude::Component for Zombies {
  type Storage = specs::storage::VecStorage<Zombies>;
}
<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

pub use self::OrderingOp::*;

use deriving::{path_local, pathvec_std, path_std};
use deriving::generic::*;
use deriving::generic::ty::*;

use syntax::ast::{self, BinOpKind, Expr, MetaItem};
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;
use syntax::symbol::Symbol;
use syntax_pos::Span;

pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt,
                                   span: Span,
                                   mitem: &MetaItem,
                                   item: &Annotatable,
                                   push: &mut FnMut(Annotatable)) {
    macro_rules! md {
        ($name:expr, $op:expr, $equal:expr) => { {
            let inline = cx.meta_word(span, Symbol::intern("inline"));
            let attrs = vec![cx.attribute(span, inline)];
            MethodDef {
                name: $name,
                generics: LifetimeBounds::empty(),
                explicit_self: borrowed_explicit_self(),
                args: vec![borrowed_self()],
                ret_ty: Literal(path_local!(bool)),
                attributes: attrs,
                is_unsafe: false,
                unify_fieldless_variants: true,
                combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
                    cs_op($op, $equal, cx, span, substr)
                }))
            }
        } }
    }

    let ordering_ty = Literal(path_std!(cx, cmp::Ordering));
    let ret_ty = Literal(Path::new_(pathvec_std!(cx, option::Option),
                                    None,
                                    vec![Box::new(ordering_ty)],
                                    PathKind::Std));

    let inline = cx.meta_word(span, Symbol::intern("inline"));
    let attrs = vec![cx.attribute(span, inline)];

    let partial_cmp_def = MethodDef {
        name: "partial_cmp",
        generics: LifetimeBounds::empty(),
        explicit_self: borrowed_explicit_self(),
        args: vec![borrowed_self()],
        ret_ty,
        attributes: attrs,
        is_unsafe: false,
        unify_fieldless_variants: true,
        combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
            cs_partial_cmp(cx, span, substr)
        })),
    };

    // avoid defining extra methods if we can
    // c-like enums, enums without any fields and structs without fields
    // can safely define only `partial_cmp`.
    let methods = if is_type_without_fields(item) {
        vec![partial_cmp_def]
    } else {
        vec![partial_cmp_def,
             md!("lt", true, false),
             md!("le", true, true),
             md!("gt", false, false),
             md!("ge", false, true)]
    };

    let trait_def = TraitDef {
        span,
        attributes: vec![],
        path: path_std!(cx, cmp::PartialOrd),
        additional_bounds: vec![],
        generics: LifetimeBounds::empty(),
        is_unsafe: false,
        supports_unions: false,
        methods,
        associated_types: Vec::new(),
    };
    trait_def.expand(cx, mitem, item, push)
}

#[derive(Copy, Clone)]
pub enum OrderingOp {
    PartialCmpOp,
    LtOp,
    LeOp,
    GtOp,
    GeOp,
}

pub fn some_ordering_collapsed(cx: &mut ExtCtxt,
                               span: Span,
                               op: OrderingOp,
                               self_arg_tags: &[ast::Ident])
                               -> P<ast::Expr> {
    let lft = cx.expr_ident(span, self_arg_tags[0]);
    let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1]));
    let op_str = match op {
        PartialCmpOp => "partial_cmp",
        LtOp => "lt",
        LeOp => "le",
        GtOp => "gt",
        GeOp => "ge",
    };
    cx.expr_method_call(span, lft, cx.ident_of(op_str), vec![rgt])
}

pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
    let test_id = cx.ident_of("__cmp");
    let ordering = cx.path_global(span, cx.std_path(&["cmp", "Ordering", "Equal"]));
    let ordering_expr = cx.expr_path(ordering.clone());
    let equals_expr = cx.expr_some(span, ordering_expr);

    let partial_cmp_path = cx.std_path(&["cmp", "PartialOrd", "partial_cmp"]);

    // Builds:
    //
    // match ::std::cmp::PartialOrd::partial_cmp(&self_field1, &other_field1) {
    // ::std::option::Option::Some(::std::cmp::Ordering::Equal) =>
    // match ::std::cmp::PartialOrd::partial_cmp(&self_field2, &other_field2) {
    // ::std::option::Option::Some(::std::cmp::Ordering::Equal) => {
    // ...
    // }
    // __cmp => __cmp
    // },
    // __cmp => __cmp
    // }
    //
    cs_fold(// foldr nests the if-elses correctly, leaving the first field
            // as the outermost one, and the last as the innermost.
            false,
            |cx, span, old, self_f, other_fs| {
        // match new {
        //     Some(::std::cmp::Ordering::Equal) => old,
        //     __cmp => __cmp
        // }

        let new = {
            let other_f = match (other_fs.len(), other_fs.get(0)) {
                (1, Some(o_f)) => o_f,
                _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"),
            };

            let args = vec![
                    cx.expr_addr_of(span, self_f),
                    cx.expr_addr_of(span, other_f.clone()),
                ];

            cx.expr_call_global(span, partial_cmp_path.clone(), args)
        };

        let eq_arm = cx.arm(span,
                            vec![cx.pat_some(span, cx.pat_path(span, ordering.clone()))],
                            old);
        let neq_arm = cx.arm(span,
                             vec![cx.pat_ident(span, test_id)],
                             cx.expr_ident(span, test_id));

        cx.expr_match(span, new, vec![eq_arm, neq_arm])
    },
            equals_expr.clone(),
            Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
        if self_args.len() != 2 {
            cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
        } else {
            some_ordering_collapsed(cx, span, PartialCmpOp, tag_tuple)
        }
    }),
            cx,
            span,
            substr)
}

/// Strict inequality.
fn cs_op(less: bool, equal: bool, cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
    let op = if less { BinOpKind::Lt } else { BinOpKind::Gt };
    cs_fold(false, // need foldr,
            |cx, span, subexpr, self_f, other_fs| {
        // build up a series of chain ||'s and &&'s from the inside
        // out (hence foldr) to get lexical ordering, i.e. for op ==
        // `ast::lt`
        //
        // ```
        // self.f1 < other.f1 || (!(other.f1 < self.f1) &&
        // (self.f2 < other.f2 || (!(other.f2 < self.f2) &&
        // (false)
        // ))
        // )
        // ```
        //
        // The optimiser should remove the redundancy. We explicitly
        // get use the binops to avoid auto-deref dereferencing too many
        // layers of pointers, if the type includes pointers.
        //
        let other_f = match (other_fs.len(), other_fs.get(0)) {
            (1, Some(o_f)) => o_f,
            _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"),
        };

        let cmp = cx.expr_binary(span, op, self_f.clone(), other_f.clone());

        let not_cmp = cx.expr_unary(span,
                                    ast::UnOp::Not,
                                    cx.expr_binary(span, op, other_f.clone(), self_f));

        let and = cx.expr_binary(span, BinOpKind::And, not_cmp, subexpr);
        cx.expr_binary(span, BinOpKind::Or, cmp, and)
    },
            cx.expr_bool(span, equal),
            Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
        if self_args.len() != 2 {
            cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
        } else {
            let op = match (less, equal) {
                (true, true) => LeOp,
                (true, false) => LtOp,
                (false, true) => GeOp,
                (false, false) => GtOp,
            };
            some_ordering_collapsed(cx, span, op, tag_tuple)
        }
    }),
            cx,
            span,
            substr)
}
<commit_msg>Fix derive(PartialOrd) implementation<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

pub use self::OrderingOp::*;

use deriving::{path_local, pathvec_std, path_std};
use deriving::generic::*;
use deriving::generic::ty::*;

use syntax::ast::{self, BinOpKind, Expr, MetaItem};
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;
use syntax::symbol::Symbol;
use syntax_pos::Span;

pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt,
                                   span: Span,
                                   mitem: &MetaItem,
                                   item: &Annotatable,
                                   push: &mut FnMut(Annotatable)) {
    macro_rules! md {
        ($name:expr, $op:expr, $equal:expr) => { {
            let inline = cx.meta_word(span, Symbol::intern("inline"));
            let attrs = vec![cx.attribute(span, inline)];
            MethodDef {
                name: $name,
                generics: LifetimeBounds::empty(),
                explicit_self: borrowed_explicit_self(),
                args: vec![borrowed_self()],
                ret_ty: Literal(path_local!(bool)),
                attributes: attrs,
                is_unsafe: false,
                unify_fieldless_variants: true,
                combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
                    cs_op($op, $equal, cx, span, substr)
                }))
            }
        } }
    }

    let ordering_ty = Literal(path_std!(cx, cmp::Ordering));
    let ret_ty = Literal(Path::new_(pathvec_std!(cx, option::Option),
                                    None,
                                    vec![Box::new(ordering_ty)],
                                    PathKind::Std));

    let inline = cx.meta_word(span, Symbol::intern("inline"));
    let attrs = vec![cx.attribute(span, inline)];

    let partial_cmp_def = MethodDef {
        name: "partial_cmp",
        generics: LifetimeBounds::empty(),
        explicit_self: borrowed_explicit_self(),
        args: vec![borrowed_self()],
        ret_ty,
        attributes: attrs,
        is_unsafe: false,
        unify_fieldless_variants: true,
        combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
            cs_partial_cmp(cx, span, substr)
        })),
    };

    // avoid defining extra methods if we can
    // c-like enums, enums without any fields and structs without fields
    // can safely define only `partial_cmp`.
    let methods = if is_type_without_fields(item) {
        vec![partial_cmp_def]
    } else {
        vec![partial_cmp_def,
             md!("lt", true, false),
             md!("le", true, true),
             md!("gt", false, false),
             md!("ge", false, true)]
    };

    let trait_def = TraitDef {
        span,
        attributes: vec![],
        path: path_std!(cx, cmp::PartialOrd),
        additional_bounds: vec![],
        generics: LifetimeBounds::empty(),
        is_unsafe: false,
        supports_unions: false,
        methods,
        associated_types: Vec::new(),
    };
    trait_def.expand(cx, mitem, item, push)
}

#[derive(Copy, Clone)]
pub enum OrderingOp {
    PartialCmpOp,
    LtOp,
    LeOp,
    GtOp,
    GeOp,
}

pub fn some_ordering_collapsed(cx: &mut ExtCtxt,
                               span: Span,
                               op: OrderingOp,
                               self_arg_tags: &[ast::Ident])
                               -> P<ast::Expr> {
    let lft = cx.expr_ident(span, self_arg_tags[0]);
    let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1]));
    let op_str = match op {
        PartialCmpOp => "partial_cmp",
        LtOp => "lt",
        LeOp => "le",
        GtOp => "gt",
        GeOp => "ge",
    };
    cx.expr_method_call(span, lft, cx.ident_of(op_str), vec![rgt])
}

pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
    let test_id = cx.ident_of("__cmp");
    let ordering = cx.path_global(span, cx.std_path(&["cmp", "Ordering", "Equal"]));
    let ordering_expr = cx.expr_path(ordering.clone());
    let equals_expr = cx.expr_some(span, ordering_expr);

    let partial_cmp_path = cx.std_path(&["cmp", "PartialOrd", "partial_cmp"]);

    // Builds:
    //
    // match ::std::cmp::PartialOrd::partial_cmp(&self_field1, &other_field1) {
    // ::std::option::Option::Some(::std::cmp::Ordering::Equal) =>
    // match ::std::cmp::PartialOrd::partial_cmp(&self_field2, &other_field2) {
    // ::std::option::Option::Some(::std::cmp::Ordering::Equal) => {
    // ...
    // }
    // __cmp => __cmp
    // },
    // __cmp => __cmp
    // }
    //
    cs_fold(// foldr nests the if-elses correctly, leaving the first field
            // as the outermost one, and the last as the innermost.
            false,
            |cx, span, old, self_f, other_fs| {
        // match new {
        //     Some(::std::cmp::Ordering::Equal) => old,
        //     __cmp => __cmp
        // }

        let new = {
            let other_f = match (other_fs.len(), other_fs.get(0)) {
                (1, Some(o_f)) => o_f,
                _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"),
            };

            let args = vec![
                    cx.expr_addr_of(span, self_f),
                    cx.expr_addr_of(span, other_f.clone()),
                ];

            cx.expr_call_global(span, partial_cmp_path.clone(), args)
        };

        let eq_arm = cx.arm(span,
                            vec![cx.pat_some(span, cx.pat_path(span, ordering.clone()))],
                            old);
        let neq_arm = cx.arm(span,
                             vec![cx.pat_ident(span, test_id)],
                             cx.expr_ident(span, test_id));

        cx.expr_match(span, new, vec![eq_arm, neq_arm])
    },
            equals_expr.clone(),
            Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
        if self_args.len() != 2 {
            cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
        } else {
            some_ordering_collapsed(cx, span, PartialCmpOp, tag_tuple)
        }
    }),
            cx,
            span,
            substr)
}

/// Strict inequality.
fn cs_op(less: bool, equal: bool, cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
    let op = if less { BinOpKind::Lt } else { BinOpKind::Gt };
    cs_fold(false, // need foldr,
            |cx, span, subexpr, self_f, other_fs| {
        // build up a series of chain ||'s and &&'s from the inside
        // out (hence foldr) to get lexical ordering, i.e. for op ==
        // `ast::lt`
        //
        // ```
        // self.f1 < other.f1 || (!(other.f1 < self.f1) &&
        // (self.f2 < other.f2 || (!(other.f2 < self.f2) &&
        // (false)
        // ))
        // )
        // ```
        //
        // The optimiser should remove the redundancy. We explicitly
        // get use the binops to avoid auto-deref dereferencing too many
        // layers of pointers, if the type includes pointers.
        //
        let other_f = match (other_fs.len(), other_fs.get(0)) {
            (1, Some(o_f)) => o_f,
            _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"),
        };

        let strict_ineq = cx.expr_binary(span, op, self_f.clone(), other_f.clone());

        let deleg_cmp = if !equal {
            cx.expr_unary(span,
                          ast::UnOp::Not,
                          cx.expr_binary(span, op, other_f.clone(), self_f))
        } else {
            cx.expr_binary(span, BinOpKind::Eq, self_f, other_f.clone())
        };

        let and = cx.expr_binary(span, BinOpKind::And, deleg_cmp, subexpr);
        cx.expr_binary(span, BinOpKind::Or, strict_ineq, and)
    },
            cx.expr_bool(span, equal),
            Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
        if self_args.len() != 2 {
            cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
        } else {
            let op = match (less, equal) {
                (true, true) => LeOp,
                (true, false) => LtOp,
                (false, true) => GeOp,
                (false, false) => GtOp,
            };
            some_ordering_collapsed(cx, span, op, tag_tuple)
        }
    }),
            cx,
            span,
            substr)
}
<|endoftext|>


<commit_before>//! Signals are a list of methods associated with a name that will be called
//! when the signal is triggered.
//!
//! Signals are stored with the object in its metatable,
//! the methods defined here are just to make it easier to use.

use super::{Object, GLOBAL_SIGNALS};
use rlua::{self, Function, Lua, Table, ToLua, ToLuaMulti, Value};

/// Connects functions to a signal. Creates a new entry in the table if it
/// doesn't exist.
pub fn connect_signal(lua: &Lua,
                      obj: Object,
                      name: String,
                      funcs: &[Function])
                      -> rlua::Result<()> {
    let signals = obj.signals()?;
    connect_signals(lua, signals, name, funcs)
}

fn connect_signals<'lua>(lua: &'lua Lua,
                         signals: Table<'lua>,
                         name: String,
                         funcs: &[Function])
                         -> rlua::Result<()> {
    if let Ok(Value::Table(table)) = signals.get::<_, Value>(name.as_str()) {
        let mut length = table.len()? + 1;
        for func in funcs {
            table.set(length, func.clone())?;
            length += 1;
        }
        Ok(())
    } else {
        let table = lua.create_table()?;
        for (index, func) in funcs.into_iter().enumerate() {
            table.set(index + 1, func.clone())?;
        }
        signals.set(name, table)
    }
}

pub fn disconnect_signal(lua: &Lua, obj: Object, name: String) -> rlua::Result<()> {
    let signals = obj.signals()?;
    disconnect_signals(lua, signals, name)
}

fn disconnect_signals(_: &Lua, signals: Table, name: String) -> rlua::Result<()> {
    signals.set(name, Value::Nil)
}

/// Evaluate the functions associated with a signal.
pub fn emit_object_signal<'lua, A>(lua: &'lua Lua,
                                   obj: Object<'lua>,
                                   name: String,
                                   args: A)
                                   -> rlua::Result<()>
    where A: ToLuaMulti<'lua> + Clone
{
    let signals = obj.signals()?;
    let mut args = args.to_lua_multi(lua)?;
    args.push_front(obj.to_lua(lua)?);
    emit_signals(lua, signals, name, args)
}

fn emit_signals<'lua, A>(_: &'lua Lua,
                         signals: Table<'lua>,
                         name: String,
                         args: A)
                         -> rlua::Result<()>
    where A: ToLuaMulti<'lua> + Clone
{
    trace!("Checking signal {}", name);
    if let Ok(Value::Table(table)) = signals.get::<_, Value>(name.clone()) {
        for entry in table.pairs::<Value, Function>() {
            if let Ok((_, func)) = entry {
                trace!("Found func for signal");
                match func.call(args.clone()) {
                    Ok(()) => {}
                    Err(e) => {
                        error!("Error while emitting signal {}: {}", name, e);
                    }
                };
            }
        }
    }
    Ok(())
}

/// Connect the function to the named signal in the global signal list.
pub fn global_connect_signal<'lua>(lua: &'lua Lua,
                                   (name, func): (String, rlua::Function<'lua>))
                                   -> rlua::Result<()> {
    let global_signals = lua.named_registry_value::<Table>(GLOBAL_SIGNALS)?;
    connect_signals(lua, global_signals, name, &[func])
}

/// Disconnect the function from the named signal in the global signal list.
pub fn global_disconnect_signal<'lua>(lua: &'lua Lua, name: String) -> rlua::Result<()> {
    let global_signals = lua.named_registry_value::<Table>(GLOBAL_SIGNALS)?;
    disconnect_signals(lua, global_signals, name)
}

/// Emit the signal with the given name from the global signal list.
pub fn global_emit_signal<'lua>(lua: &'lua Lua, (name, args): (String, Value)) -> rlua::Result<()> {
    let global_signals = lua.named_registry_value::<Table>(GLOBAL_SIGNALS)?;
    emit_signals(lua, global_signals, name, args)
}
<commit_msg>Removed annoying signal debug log<commit_after>//! Signals are a list of methods associated with a name that will be called
//! when the signal is triggered.
//!
//! Signals are stored with the object in its metatable,
//! the methods defined here are just to make it easier to use.

use super::{Object, GLOBAL_SIGNALS};
use rlua::{self, Function, Lua, Table, ToLua, ToLuaMulti, Value};

/// Connects functions to a signal. Creates a new entry in the table if it
/// doesn't exist.
pub fn connect_signal(lua: &Lua,
                      obj: Object,
                      name: String,
                      funcs: &[Function])
                      -> rlua::Result<()> {
    let signals = obj.signals()?;
    connect_signals(lua, signals, name, funcs)
}

fn connect_signals<'lua>(lua: &'lua Lua,
                         signals: Table<'lua>,
                         name: String,
                         funcs: &[Function])
                         -> rlua::Result<()> {
    if let Ok(Value::Table(table)) = signals.get::<_, Value>(name.as_str()) {
        let mut length = table.len()? + 1;
        for func in funcs {
            table.set(length, func.clone())?;
            length += 1;
        }
        Ok(())
    } else {
        let table = lua.create_table()?;
        for (index, func) in funcs.into_iter().enumerate() {
            table.set(index + 1, func.clone())?;
        }
        signals.set(name, table)
    }
}

pub fn disconnect_signal(lua: &Lua, obj: Object, name: String) -> rlua::Result<()> {
    let signals = obj.signals()?;
    disconnect_signals(lua, signals, name)
}

fn disconnect_signals(_: &Lua, signals: Table, name: String) -> rlua::Result<()> {
    signals.set(name, Value::Nil)
}

/// Evaluate the functions associated with a signal.
pub fn emit_object_signal<'lua, A>(lua: &'lua Lua,
                                   obj: Object<'lua>,
                                   name: String,
                                   args: A)
                                   -> rlua::Result<()>
    where A: ToLuaMulti<'lua> + Clone
{
    let signals = obj.signals()?;
    let mut args = args.to_lua_multi(lua)?;
    args.push_front(obj.to_lua(lua)?);
    emit_signals(lua, signals, name, args)
}

fn emit_signals<'lua, A>(_: &'lua Lua,
                         signals: Table<'lua>,
                         name: String,
                         args: A)
                         -> rlua::Result<()>
    where A: ToLuaMulti<'lua> + Clone
{
    if let Ok(Value::Table(table)) = signals.get::<_, Value>(name.clone()) {
        for entry in table.pairs::<Value, Function>() {
            if let Ok((_, func)) = entry {
                match func.call(args.clone()) {
                    Ok(()) => {}
                    Err(e) => {
                        error!("Error while emitting signal {}: {}", name, e);
                    }
                };
            }
        }
    }
    Ok(())
}

/// Connect the function to the named signal in the global signal list.
pub fn global_connect_signal<'lua>(lua: &'lua Lua,
                                   (name, func): (String, rlua::Function<'lua>))
                                   -> rlua::Result<()> {
    let global_signals = lua.named_registry_value::<Table>(GLOBAL_SIGNALS)?;
    connect_signals(lua, global_signals, name, &[func])
}

/// Disconnect the function from the named signal in the global signal list.
pub fn global_disconnect_signal<'lua>(lua: &'lua Lua, name: String) -> rlua::Result<()> {
    let global_signals = lua.named_registry_value::<Table>(GLOBAL_SIGNALS)?;
    disconnect_signals(lua, global_signals, name)
}

/// Emit the signal with the given name from the global signal list.
pub fn global_emit_signal<'lua>(lua: &'lua Lua, (name, args): (String, Value)) -> rlua::Result<()> {
    let global_signals = lua.named_registry_value::<Table>(GLOBAL_SIGNALS)?;
    emit_signals(lua, global_signals, name, args)
}
<|endoftext|>


<commit_before>// Copyright 2018 The Grin Developers
//
// 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.

/// Grin server commands processing
use std::env::current_dir;
use std::process::exit;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;

use clap::ArgMatches;
use ctrlc;
use daemonize::Daemonize;

use config::GlobalConfig;
use core::global;
use p2p::Seeding;
use servers;
use tui::ui;

/// wrap below to allow UI to clean up on stop
fn start_server(config: servers::ServerConfig) {
	start_server_tui(config);
	// Just kill process for now, otherwise the process
	// hangs around until sigint because the API server
	// currently has no shutdown facility
	warn!("Shutting down...");
	thread::sleep(Duration::from_millis(1000));
	warn!("Shutdown complete.");
	exit(0);
}

fn start_server_tui(config: servers::ServerConfig) {
	// Run the UI controller.. here for now for simplicity to access
	// everything it might need
	if config.run_tui.is_some() && config.run_tui.unwrap() {
		warn!("Starting GRIN in UI mode...");
		servers::Server::start(config, |serv: Arc<servers::Server>| {
			let running = Arc::new(AtomicBool::new(true));
			let _ = thread::Builder::new()
				.name("ui".to_string())
				.spawn(move || {
					let mut controller = ui::Controller::new().unwrap_or_else(|e| {
						panic!("Error loading UI controller: {}", e);
					});
					controller.run(serv.clone(), running);
				});
		}).unwrap();
	} else {
		warn!("Starting GRIN w/o UI...");
		servers::Server::start(config, |serv: Arc<servers::Server>| {
			let running = Arc::new(AtomicBool::new(true));
			let r = running.clone();
			ctrlc::set_handler(move || {
				r.store(false, Ordering::SeqCst);
			}).expect("Error setting handler for both SIGINT (Ctrl+C) and SIGTERM (kill)");
			while running.load(Ordering::SeqCst) {
				thread::sleep(Duration::from_secs(1));
			}
			warn!("Received SIGINT (Ctrl+C) or SIGTERM (kill).");
			serv.stop();
		}).unwrap();
	}
}

/// Handles the server part of the command line, mostly running, starting and
/// stopping the Grin blockchain server. Processes all the command line
/// arguments to build a proper configuration and runs Grin with that
/// configuration.
pub fn server_command(server_args: Option<&ArgMatches>, mut global_config: GlobalConfig) -> i32 {
	global::set_mining_mode(
		global_config
			.members
			.as_mut()
			.unwrap()
			.server
			.clone()
			.chain_type,
	);

	// just get defaults from the global config
	let mut server_config = global_config.members.as_ref().unwrap().server.clone();

	if let Some(a) = server_args {
		if let Some(port) = a.value_of("port") {
			server_config.p2p_config.port = port.parse().unwrap();
		}

		if let Some(api_port) = a.value_of("api_port") {
			let default_ip = "0.0.0.0";
			server_config.api_http_addr = format!("{}:{}", default_ip, api_port);
		}

		if let Some(wallet_url) = a.value_of("wallet_url") {
			server_config
				.stratum_mining_config
				.as_mut()
				.unwrap()
				.wallet_listener_url = wallet_url.to_string();
		}

		if let Some(seeds) = a.values_of("seed") {
			server_config.p2p_config.seeding_type = Seeding::List;
			server_config.p2p_config.seeds = Some(seeds.map(|s| s.to_string()).collect());
		}
	}

	/*if let Some(true) = server_config.run_wallet_listener {
		let mut wallet_config = global_config.members.as_ref().unwrap().wallet.clone();
		wallet::init_wallet_seed(wallet_config.clone());
		let wallet = wallet::instantiate_wallet(wallet_config.clone(), "");

		let _ = thread::Builder::new()
			.name("wallet_listener".to_string())
			.spawn(move || {
				controller::foreign_listener(wallet, &wallet_config.api_listen_addr())
					.unwrap_or_else(|e| {
						panic!(
							"Error creating wallet listener: {:?} Config: {:?}",
							e, wallet_config
						)
					});
			});
	}
	if let Some(true) = server_config.run_wallet_owner_api {
		let mut wallet_config = global_config.members.unwrap().wallet;
		let wallet = wallet::instantiate_wallet(wallet_config.clone(), "");
		wallet::init_wallet_seed(wallet_config.clone());

		let _ = thread::Builder::new()
			.name("wallet_owner_listener".to_string())
			.spawn(move || {
				controller::owner_listener(wallet, "127.0.0.1:13420").unwrap_or_else(|e| {
					panic!(
						"Error creating wallet api listener: {:?} Config: {:?}",
						e, wallet_config
					)
				});
			});
	}*/

	// start the server in the different run modes (interactive or daemon)
	if let Some(a) = server_args {
		match a.subcommand() {
			("run", _) => {
				start_server(server_config);
			}
			("start", _) => {
				let daemonize = Daemonize::new()
					.pid_file("/tmp/grin.pid")
					.chown_pid_file(true)
					.working_directory(current_dir().unwrap())
					.privileged_action(move || {
						start_server(server_config.clone());
						loop {
							thread::sleep(Duration::from_secs(60));
						}
					});
				match daemonize.start() {
					Ok(_) => info!("Grin server successfully started."),
					Err(e) => error!("Error starting: {}", e),
				}
			}
			("stop", _) => println!("TODO. Just 'kill $pid' for now. Maybe /tmp/grin.pid is $pid"),
			(cmd, _) => {
				println!(":: {:?}", server_args);
				panic!(
					"Unknown server command '{}', use 'grin help server' for details",
					cmd
				);
			}
		}
	} else {
		start_server(server_config);
	}
	0
}
<commit_msg>Improve serve command error message (#2017)<commit_after>// Copyright 2018 The Grin Developers
//
// 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.

/// Grin server commands processing
use std::env::current_dir;
use std::process::exit;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;

use clap::ArgMatches;
use ctrlc;
use daemonize::Daemonize;

use config::GlobalConfig;
use core::global;
use p2p::Seeding;
use servers;
use tui::ui;

/// wrap below to allow UI to clean up on stop
fn start_server(config: servers::ServerConfig) {
	start_server_tui(config);
	// Just kill process for now, otherwise the process
	// hangs around until sigint because the API server
	// currently has no shutdown facility
	warn!("Shutting down...");
	thread::sleep(Duration::from_millis(1000));
	warn!("Shutdown complete.");
	exit(0);
}

fn start_server_tui(config: servers::ServerConfig) {
	// Run the UI controller.. here for now for simplicity to access
	// everything it might need
	if config.run_tui.is_some() && config.run_tui.unwrap() {
		warn!("Starting GRIN in UI mode...");
		servers::Server::start(config, |serv: Arc<servers::Server>| {
			let running = Arc::new(AtomicBool::new(true));
			let _ = thread::Builder::new()
				.name("ui".to_string())
				.spawn(move || {
					let mut controller = ui::Controller::new().unwrap_or_else(|e| {
						panic!("Error loading UI controller: {}", e);
					});
					controller.run(serv.clone(), running);
				});
		}).unwrap();
	} else {
		warn!("Starting GRIN w/o UI...");
		servers::Server::start(config, |serv: Arc<servers::Server>| {
			let running = Arc::new(AtomicBool::new(true));
			let r = running.clone();
			ctrlc::set_handler(move || {
				r.store(false, Ordering::SeqCst);
			}).expect("Error setting handler for both SIGINT (Ctrl+C) and SIGTERM (kill)");
			while running.load(Ordering::SeqCst) {
				thread::sleep(Duration::from_secs(1));
			}
			warn!("Received SIGINT (Ctrl+C) or SIGTERM (kill).");
			serv.stop();
		}).unwrap();
	}
}

/// Handles the server part of the command line, mostly running, starting and
/// stopping the Grin blockchain server. Processes all the command line
/// arguments to build a proper configuration and runs Grin with that
/// configuration.
pub fn server_command(server_args: Option<&ArgMatches>, mut global_config: GlobalConfig) -> i32 {
	global::set_mining_mode(
		global_config
			.members
			.as_mut()
			.unwrap()
			.server
			.clone()
			.chain_type,
	);

	// just get defaults from the global config
	let mut server_config = global_config.members.as_ref().unwrap().server.clone();

	if let Some(a) = server_args {
		if let Some(port) = a.value_of("port") {
			server_config.p2p_config.port = port.parse().unwrap();
		}

		if let Some(api_port) = a.value_of("api_port") {
			let default_ip = "0.0.0.0";
			server_config.api_http_addr = format!("{}:{}", default_ip, api_port);
		}

		if let Some(wallet_url) = a.value_of("wallet_url") {
			server_config
				.stratum_mining_config
				.as_mut()
				.unwrap()
				.wallet_listener_url = wallet_url.to_string();
		}

		if let Some(seeds) = a.values_of("seed") {
			server_config.p2p_config.seeding_type = Seeding::List;
			server_config.p2p_config.seeds = Some(seeds.map(|s| s.to_string()).collect());
		}
	}

	/*if let Some(true) = server_config.run_wallet_listener {
		let mut wallet_config = global_config.members.as_ref().unwrap().wallet.clone();
		wallet::init_wallet_seed(wallet_config.clone());
		let wallet = wallet::instantiate_wallet(wallet_config.clone(), "");

		let _ = thread::Builder::new()
			.name("wallet_listener".to_string())
			.spawn(move || {
				controller::foreign_listener(wallet, &wallet_config.api_listen_addr())
					.unwrap_or_else(|e| {
						panic!(
							"Error creating wallet listener: {:?} Config: {:?}",
							e, wallet_config
						)
					});
			});
	}
	if let Some(true) = server_config.run_wallet_owner_api {
		let mut wallet_config = global_config.members.unwrap().wallet;
		let wallet = wallet::instantiate_wallet(wallet_config.clone(), "");
		wallet::init_wallet_seed(wallet_config.clone());

		let _ = thread::Builder::new()
			.name("wallet_owner_listener".to_string())
			.spawn(move || {
				controller::owner_listener(wallet, "127.0.0.1:13420").unwrap_or_else(|e| {
					panic!(
						"Error creating wallet api listener: {:?} Config: {:?}",
						e, wallet_config
					)
				});
			});
	}*/

	// start the server in the different run modes (interactive or daemon)
	if let Some(a) = server_args {
		match a.subcommand() {
			("run", _) => {
				start_server(server_config);
			}
			("start", _) => {
				let daemonize = Daemonize::new()
					.pid_file("/tmp/grin.pid")
					.chown_pid_file(true)
					.working_directory(current_dir().unwrap())
					.privileged_action(move || {
						start_server(server_config.clone());
						loop {
							thread::sleep(Duration::from_secs(60));
						}
					});
				match daemonize.start() {
					Ok(_) => info!("Grin server successfully started."),
					Err(e) => error!("Error starting: {}", e),
				}
			}
			("stop", _) => println!("TODO. Just 'kill $pid' for now. Maybe /tmp/grin.pid is $pid"),
			("", _) => {
				println!("Subcommand required, use 'grin help server' for details");
			}
			(cmd, _) => {
				println!(":: {:?}", server_args);
				panic!(
					"Unknown server command '{}', use 'grin help server' for details",
					cmd
				);
			}
		}
	} else {
		start_server(server_config);
	}
	0
}
<|endoftext|>