<commit_before>// 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.

use std::ffi::CStr;
use std::fmt;
use super::broadcaster::SBBroadcaster;
use super::error::SBError;
use super::event::SBEvent;
use super::stream::SBStream;
use super::thread::SBThread;
use super::{lldb_pid_t, lldb_tid_t, StateType};
use sys;

/// The process associated with the target program.
///
/// You get a process by attaching to or launching a target program.
/// See [`SBTarget`] for details.
///
/// # Process State
///
/// The OS process ID (`pid_t`) for the process is available via
/// [`process_id`].
///
/// The [process state] can be obtained via [`state`]. It is common to
/// just check to see if the process [`is_alive`], [`is_running`] or
/// [`is_stopped`].
///
/// Once the process is in the `Exited` state, the [`exit_status`] and
/// [`exit_description`] are available for inspection.
///
/// # Execution Control
///
/// Once you have a process, you can:
///
/// * [`continue_execution`]
/// * [`stop`]
/// * [`kill`]
/// * [`detach`]
///
/// # Threads
///
/// The process contains the [threads of execution] for the [target]. The
/// available threads can be iterated over with [`threads`]:
///
/// ```no_run
/// # use lldb::{SBProcess, SBThread};
/// # fn look_at_threads(process: SBProcess) {
/// // Iterate over the threads...
/// for thread in process.threads() {
///     println!("Hello {}!", thread.thread_id());
/// }
/// // Or collect them into a vector!
/// let threads = process.threads().collect::<Vec<SBThread>>();
/// # }
/// ```
///
/// Specific individual threads can be looked up via [`thread_by_id`]
/// and [`thread_by_index_id`] methods.
///
/// Some functions operate on the 'currently selected thread'. This can
/// retrieved via [`selected_thread`] and set via [`set_selected_thread`],
/// [`set_selected_thread_by_id`], or [`set_selected_thread_by_index_id`].
///
/// # Events
///
/// ... to be written ...
///
/// [`SBTarget`]: struct.SBTarget.html
/// [`process_id`]: #method.process_id
/// [process state]: enum.StateType.html
/// [`state`]: #method.state
/// [`is_alive`]: #method.is_alive
/// [`is_running`]: #method.is_running
/// [`is_stopped`]: #method.is_stopped
/// [`exit_status`]: #method.exit_status
/// [`exit_description`]: #method.exit_description
/// [`continue_execution`]: #method.continue_execution
/// [`stop`]: #method.stop
/// [`kill`]: #method.kill
/// [`detach`]: #method.detach
/// [threads of execution]: struct.SBThread.html
/// [target]: struct.SBTarget.html
/// [`threads`]: #method.threads
/// [`thread_by_id`]: #method.thread_by_id
/// [`thread_by_index_id`]: #method.thread_by_index_id
/// [`selected_thread`]: #method.selected_thread
/// [`set_selected_thread`]: #method.set_selected_thread
/// [`set_selected_thread_by_id`]: #method.set_selected_thread_by_id
/// [`set_selected_thread_by_index_id`]: #method.set_selected_thread_by_index_id
pub struct SBProcess {
    /// The underlying raw `SBProcessRef`.
    pub raw: sys::SBProcessRef,
}

impl SBProcess {
    /// Construct a new `SBProcess`.
    pub fn wrap(raw: sys::SBProcessRef) -> SBProcess {
        SBProcess { raw: raw }
    }

    /// Construct a new `Some(SBProcess)` or `None`.
    pub fn maybe_wrap(raw: sys::SBProcessRef) -> Option<SBProcess> {
        if unsafe { sys::SBProcessIsValid(raw) != 0 } {
            Some(SBProcess { raw: raw })
        } else {
            None
        }
    }

    /// Check whether or not this is a valid `SBProcess` value.
    pub fn is_valid(&self) -> bool {
        unsafe { sys::SBProcessIsValid(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn broadcaster_class_name() -> &'static str {
        unsafe {
            match CStr::from_ptr(sys::SBProcessGetBroadcasterClassName()).to_str() {
                Ok(s) => s,
                _ => panic!("Invalid string?"),
            }
        }
    }

    /// The current state of this process (running, stopped, exited, etc.).
    pub fn state(&self) -> StateType {
        unsafe { sys::SBProcessGetState(self.raw) }
    }

    /// Returns `true` if the process is currently alive.
    ///
    /// This corresponds to the process being in the `Attaching`,
    /// `Launching`, `Stopped`, `Running`, `Stepping`, `Crashed`
    /// or `Suspended` states.
    pub fn is_alive(&self) -> bool {
        match self.state() {
            StateType::Attaching | StateType::Launching | StateType::Stopped |
            StateType::Running | StateType::Stepping | StateType::Crashed |
            StateType::Suspended => true,
            _ => false,
        }
    }

    /// Returns `true` if the process is currently running.
    ///
    /// This corresponds to the process being in the `Running`
    /// or `Stepping` states.
    pub fn is_running(&self) -> bool {
        match self.state() {
            StateType::Running | StateType::Stepping => true,
            _ => false,
        }
    }

    /// Returns `true` if the process is currently stopped.
    ///
    /// This corresponds to the process being in the `Stopped`, `Crashed`,
    /// or `Suspended` states.
    pub fn is_stopped(&self) -> bool {
        match self.state() {
            StateType::Stopped | StateType::Crashed | StateType::Suspended => true,
            _ => false,
        }
    }

    /// The exit status of the process when the process state is
    /// `Exited`.
    pub fn exit_status(&self) -> i32 {
        unsafe { sys::SBProcessGetExitStatus(self.raw) }
    }

    /// The exit description of the process when the process state
    /// is `Exited`.
    pub fn exit_description(&self) -> &str {
        unsafe {
            match CStr::from_ptr(sys::SBProcessGetExitDescription(self.raw)).to_str() {
                Ok(s) => s,
                _ => panic!("Invalid string?"),
            }
        }
    }

    /// Returns the process ID of the process.
    pub fn process_id(&self) -> lldb_pid_t {
        unsafe { sys::SBProcessGetProcessID(self.raw) }
    }

    /// Returns an integer ID that is guaranteed to be unique across all
    /// process instances. This is not the process ID, just a unique
    /// integer for comparison and caching purposes.
    pub fn unique_id(&self) -> u32 {
        unsafe { sys::SBProcessGetUniqueID(self.raw) }
    }

    /// Get the size, in bytes, of an address.
    pub fn address_byte_size(&self) -> u32 {
        unsafe { sys::SBProcessGetAddressByteSize(self.raw) }
    }

    /// Kills the process and shuts down all threads that were spawned to
    /// track and monitor the process.
    pub fn destroy(&self) -> Result<(), SBError> {
        let error = SBError::wrap(unsafe { sys::SBProcessDestroy(self.raw) });
        if error.is_success() {
            Ok(())
        } else {
            Err(error)
        }
    }

    #[allow(missing_docs)]
    pub fn continue_execution(&self) -> Result<(), SBError> {
        let error = SBError::wrap(unsafe { sys::SBProcessContinue(self.raw) });
        if error.is_success() {
            Ok(())
        } else {
            Err(error)
        }
    }

    #[allow(missing_docs)]
    pub fn stop(&self) -> Result<(), SBError> {
        let error = SBError::wrap(unsafe { sys::SBProcessStop(self.raw) });
        if error.is_success() {
            Ok(())
        } else {
            Err(error)
        }
    }

    /// Same as calling `destroy`.
    pub fn kill(&self) -> Result<(), SBError> {
        let error = SBError::wrap(unsafe { sys::SBProcessKill(self.raw) });
        if error.is_success() {
            Ok(())
        } else {
            Err(error)
        }
    }

    #[allow(missing_docs)]
    pub fn detach(&self) -> Result<(), SBError> {
        let error = SBError::wrap(unsafe { sys::SBProcessDetach(self.raw) });
        if error.is_success() {
            Ok(())
        } else {
            Err(error)
        }
    }

    /// Send the process a Unix signal.
    pub fn signal(&self, signal: i32) -> Result<(), SBError> {
        let error = SBError::wrap(unsafe { sys::SBProcessSignal(self.raw, signal) });
        if error.is_success() {
            Ok(())
        } else {
            Err(error)
        }
    }

    #[allow(missing_docs)]
    pub fn broadcaster(&self) -> SBBroadcaster {
        SBBroadcaster::wrap(unsafe { sys::SBProcessGetBroadcaster(self.raw) })
    }

    /// Get an iterator over the [threads] known to this process instance.
    ///
    /// [threads]: struct.SBThread.html
    pub fn threads(&self) -> SBProcessThreadIter {
        SBProcessThreadIter {
            process: self,
            idx: 0,
        }
    }

    /// Returns the thread with the given thread ID.
    pub fn thread_by_id(&self, thread_id: lldb_tid_t) -> Option<SBThread> {
        SBThread::maybe_wrap(unsafe { sys::SBProcessGetThreadByID(self.raw, thread_id) })
    }

    /// Returns the thread with the given thread index ID.
    pub fn thread_by_index_id(&self, thread_index_id: u32) -> Option<SBThread> {
        SBThread::maybe_wrap(unsafe { sys::SBProcessGetThreadByIndexID(self.raw, thread_index_id) })
    }

    /// Returns the currently selected thread.
    pub fn selected_thread(&self) -> SBThread {
        SBThread::wrap(unsafe { sys::SBProcessGetSelectedThread(self.raw) })
    }

    /// Set the selected thread.
    pub fn set_selected_thread(&self, thread: &SBThread) -> bool {
        unsafe { sys::SBProcessSetSelectedThread(self.raw, thread.raw) != 0 }
    }

    /// Set the selected thread by ID.
    pub fn set_selected_thread_by_id(&self, thread_id: lldb_tid_t) -> bool {
        unsafe { sys::SBProcessSetSelectedThreadByID(self.raw, thread_id) != 0 }
    }

    /// Set the selected thread by index ID.
    pub fn set_selected_thread_by_index_id(&self, thread_index_id: u32) -> bool {
        unsafe { sys::SBProcessSetSelectedThreadByIndexID(self.raw, thread_index_id) != 0 }
    }

    #[allow(missing_docs)]
    pub fn event_as_process_event(event: &SBEvent) -> Option<SBProcessEvent> {
        if unsafe { sys::SBProcessEventIsProcessEvent(event.raw) != 0 } {
            Some(SBProcessEvent::new(event))
        } else {
            None
        }
    }
}

/// Iterate over the [threads] in a [process].
///
/// [threads]: struct.SBThread.html
/// [process]: struct.SBProcess.html
pub struct SBProcessThreadIter<'d> {
    process: &'d SBProcess,
    idx: usize,
}

impl<'d> Iterator for SBProcessThreadIter<'d> {
    type Item = SBThread;

    fn next(&mut self) -> Option<SBThread> {
        if self.idx < unsafe { sys::SBProcessGetNumThreads(self.process.raw) as usize } {
            let r = Some(SBThread::wrap(unsafe {
                                            sys::SBProcessGetThreadAtIndex(self.process.raw,
                                                                           self.idx)
                                        }));
            self.idx += 1;
            r
        } else {
            None
        }
    }
}

impl fmt::Debug for SBProcess {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let stream = SBStream::new();
        unsafe { sys::SBProcessGetDescription(self.raw, stream.raw) };
        write!(fmt, "SBProcess {{ {} }}", stream.data())
    }
}

impl Drop for SBProcess {
    fn drop(&mut self) {
        unsafe { sys::DisposeSBProcess(self.raw) };
    }
}

#[allow(missing_docs)]
pub struct SBProcessEvent<'e> {
    event: &'e SBEvent,
}

#[allow(missing_docs)]
impl<'e> SBProcessEvent<'e> {
    pub fn new(event: &'e SBEvent) -> Self {
        SBProcessEvent { event: event }
    }

    pub fn process_state(&self) -> StateType {
        unsafe { sys::SBProcessGetStateFromEvent(self.event.raw) }
    }

    pub fn process(&self) -> SBProcess {
        SBProcess::wrap(unsafe { sys::SBProcessGetProcessFromEvent(self.event.raw) })
    }

    pub fn interrupted(&self) -> bool {
        unsafe { sys::SBProcessGetInterruptedFromEvent(self.event.raw) != 0 }
    }

    pub fn restarted(&self) -> bool {
        unsafe { sys::SBProcessGetRestartedFromEvent(self.event.raw) != 0 }
    }

    pub fn restarted_reasons(&self) -> SBProcessEventRestartedReasonIter {
        SBProcessEventRestartedReasonIter {
            event: self,
            idx: 0,
        }
    }
}

/// Iterate over the restart reasons in a [process event].
///
/// [process event]: struct.SBProcessEvent.html
pub struct SBProcessEventRestartedReasonIter<'d> {
    event: &'d SBProcessEvent<'d>,
    idx: usize,
}

impl<'d> Iterator for SBProcessEventRestartedReasonIter<'d> {
    type Item = &'d str;

    fn next(&mut self) -> Option<&'d str> {
        let raw = self.event.event.raw;
        if self.idx < unsafe { sys::SBProcessGetNumRestartedReasonsFromEvent(raw) as usize } {
            let r = unsafe {
                let s = CStr::from_ptr(sys::SBProcessGetRestartedReasonAtIndexFromEvent(raw,
                                                                                self.idx));
                match s.to_str() {
                    Ok(s) => s,
                    _ => panic!("Invalid string?"),
                }
            };
            self.idx += 1;
            Some(r)
        } else {
            None
        }
    }
}

#[cfg(feature = "graphql")]
graphql_object!(SBProcess: super::debugger::SBDebugger | &self | {
    field is_valid() -> bool {
        self.is_valid()
    }

    field is_alive() -> bool {
        self.is_alive()
    }

    field is_running() -> bool {
        self.is_running()
    }

    field is_stopped() -> bool {
        self.is_stopped()
    }

    // TODO(bm) This should be i32
    field exit_status() -> i64 {
        self.exit_status() as i64
    }

    field exit_description() -> &str {
        self.exit_description()
    }

    // TODO(bm): This should be u64
    field process_id() -> i64 {
        self.process_id() as i64
    }

    // TODO(bm) This should be u32
    field unique_id() -> i64 {
        self.unique_id() as i64
    }

    // TODO(bm) This should be u32
    field address_byte_size() -> i64 {
        self.address_byte_size() as i64
    }

    field threads() -> Vec<SBThread> {
        self.threads().collect()
    }

    field selected_thread() -> SBThread {
        self.selected_thread()
    }
});
<commit_msg>Bind SBProcess:save_core().<commit_after>// 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.

use std::ffi::{CStr, CString};
use std::fmt;
use super::broadcaster::SBBroadcaster;
use super::error::SBError;
use super::event::SBEvent;
use super::stream::SBStream;
use super::thread::SBThread;
use super::{lldb_pid_t, lldb_tid_t, StateType};
use sys;

/// The process associated with the target program.
///
/// You get a process by attaching to or launching a target program.
/// See [`SBTarget`] for details.
///
/// # Process State
///
/// The OS process ID (`pid_t`) for the process is available via
/// [`process_id`].
///
/// The [process state] can be obtained via [`state`]. It is common to
/// just check to see if the process [`is_alive`], [`is_running`] or
/// [`is_stopped`].
///
/// Once the process is in the `Exited` state, the [`exit_status`] and
/// [`exit_description`] are available for inspection.
///
/// # Execution Control
///
/// Once you have a process, you can:
///
/// * [`continue_execution`]
/// * [`stop`]
/// * [`kill`]
/// * [`detach`]
///
/// # Threads
///
/// The process contains the [threads of execution] for the [target]. The
/// available threads can be iterated over with [`threads`]:
///
/// ```no_run
/// # use lldb::{SBProcess, SBThread};
/// # fn look_at_threads(process: SBProcess) {
/// // Iterate over the threads...
/// for thread in process.threads() {
///     println!("Hello {}!", thread.thread_id());
/// }
/// // Or collect them into a vector!
/// let threads = process.threads().collect::<Vec<SBThread>>();
/// # }
/// ```
///
/// Specific individual threads can be looked up via [`thread_by_id`]
/// and [`thread_by_index_id`] methods.
///
/// Some functions operate on the 'currently selected thread'. This can
/// retrieved via [`selected_thread`] and set via [`set_selected_thread`],
/// [`set_selected_thread_by_id`], or [`set_selected_thread_by_index_id`].
///
/// # Events
///
/// ... to be written ...
///
/// [`SBTarget`]: struct.SBTarget.html
/// [`process_id`]: #method.process_id
/// [process state]: enum.StateType.html
/// [`state`]: #method.state
/// [`is_alive`]: #method.is_alive
/// [`is_running`]: #method.is_running
/// [`is_stopped`]: #method.is_stopped
/// [`exit_status`]: #method.exit_status
/// [`exit_description`]: #method.exit_description
/// [`continue_execution`]: #method.continue_execution
/// [`stop`]: #method.stop
/// [`kill`]: #method.kill
/// [`detach`]: #method.detach
/// [threads of execution]: struct.SBThread.html
/// [target]: struct.SBTarget.html
/// [`threads`]: #method.threads
/// [`thread_by_id`]: #method.thread_by_id
/// [`thread_by_index_id`]: #method.thread_by_index_id
/// [`selected_thread`]: #method.selected_thread
/// [`set_selected_thread`]: #method.set_selected_thread
/// [`set_selected_thread_by_id`]: #method.set_selected_thread_by_id
/// [`set_selected_thread_by_index_id`]: #method.set_selected_thread_by_index_id
pub struct SBProcess {
    /// The underlying raw `SBProcessRef`.
    pub raw: sys::SBProcessRef,
}

impl SBProcess {
    /// Construct a new `SBProcess`.
    pub fn wrap(raw: sys::SBProcessRef) -> SBProcess {
        SBProcess { raw: raw }
    }

    /// Construct a new `Some(SBProcess)` or `None`.
    pub fn maybe_wrap(raw: sys::SBProcessRef) -> Option<SBProcess> {
        if unsafe { sys::SBProcessIsValid(raw) != 0 } {
            Some(SBProcess { raw: raw })
        } else {
            None
        }
    }

    /// Check whether or not this is a valid `SBProcess` value.
    pub fn is_valid(&self) -> bool {
        unsafe { sys::SBProcessIsValid(self.raw) != 0 }
    }

    #[allow(missing_docs)]
    pub fn broadcaster_class_name() -> &'static str {
        unsafe {
            match CStr::from_ptr(sys::SBProcessGetBroadcasterClassName()).to_str() {
                Ok(s) => s,
                _ => panic!("Invalid string?"),
            }
        }
    }

    /// The current state of this process (running, stopped, exited, etc.).
    pub fn state(&self) -> StateType {
        unsafe { sys::SBProcessGetState(self.raw) }
    }

    /// Returns `true` if the process is currently alive.
    ///
    /// This corresponds to the process being in the `Attaching`,
    /// `Launching`, `Stopped`, `Running`, `Stepping`, `Crashed`
    /// or `Suspended` states.
    pub fn is_alive(&self) -> bool {
        match self.state() {
            StateType::Attaching | StateType::Launching | StateType::Stopped |
            StateType::Running | StateType::Stepping | StateType::Crashed |
            StateType::Suspended => true,
            _ => false,
        }
    }

    /// Returns `true` if the process is currently running.
    ///
    /// This corresponds to the process being in the `Running`
    /// or `Stepping` states.
    pub fn is_running(&self) -> bool {
        match self.state() {
            StateType::Running | StateType::Stepping => true,
            _ => false,
        }
    }

    /// Returns `true` if the process is currently stopped.
    ///
    /// This corresponds to the process being in the `Stopped`, `Crashed`,
    /// or `Suspended` states.
    pub fn is_stopped(&self) -> bool {
        match self.state() {
            StateType::Stopped | StateType::Crashed | StateType::Suspended => true,
            _ => false,
        }
    }

    /// The exit status of the process when the process state is
    /// `Exited`.
    pub fn exit_status(&self) -> i32 {
        unsafe { sys::SBProcessGetExitStatus(self.raw) }
    }

    /// The exit description of the process when the process state
    /// is `Exited`.
    pub fn exit_description(&self) -> &str {
        unsafe {
            match CStr::from_ptr(sys::SBProcessGetExitDescription(self.raw)).to_str() {
                Ok(s) => s,
                _ => panic!("Invalid string?"),
            }
        }
    }

    /// Returns the process ID of the process.
    pub fn process_id(&self) -> lldb_pid_t {
        unsafe { sys::SBProcessGetProcessID(self.raw) }
    }

    /// Returns an integer ID that is guaranteed to be unique across all
    /// process instances. This is not the process ID, just a unique
    /// integer for comparison and caching purposes.
    pub fn unique_id(&self) -> u32 {
        unsafe { sys::SBProcessGetUniqueID(self.raw) }
    }

    /// Get the size, in bytes, of an address.
    pub fn address_byte_size(&self) -> u32 {
        unsafe { sys::SBProcessGetAddressByteSize(self.raw) }
    }

    /// Kills the process and shuts down all threads that were spawned to
    /// track and monitor the process.
    pub fn destroy(&self) -> Result<(), SBError> {
        let error = SBError::wrap(unsafe { sys::SBProcessDestroy(self.raw) });
        if error.is_success() {
            Ok(())
        } else {
            Err(error)
        }
    }

    #[allow(missing_docs)]
    pub fn continue_execution(&self) -> Result<(), SBError> {
        let error = SBError::wrap(unsafe { sys::SBProcessContinue(self.raw) });
        if error.is_success() {
            Ok(())
        } else {
            Err(error)
        }
    }

    #[allow(missing_docs)]
    pub fn stop(&self) -> Result<(), SBError> {
        let error = SBError::wrap(unsafe { sys::SBProcessStop(self.raw) });
        if error.is_success() {
            Ok(())
        } else {
            Err(error)
        }
    }

    /// Same as calling `destroy`.
    pub fn kill(&self) -> Result<(), SBError> {
        let error = SBError::wrap(unsafe { sys::SBProcessKill(self.raw) });
        if error.is_success() {
            Ok(())
        } else {
            Err(error)
        }
    }

    #[allow(missing_docs)]
    pub fn detach(&self) -> Result<(), SBError> {
        let error = SBError::wrap(unsafe { sys::SBProcessDetach(self.raw) });
        if error.is_success() {
            Ok(())
        } else {
            Err(error)
        }
    }

    /// Send the process a Unix signal.
    pub fn signal(&self, signal: i32) -> Result<(), SBError> {
        let error = SBError::wrap(unsafe { sys::SBProcessSignal(self.raw, signal) });
        if error.is_success() {
            Ok(())
        } else {
            Err(error)
        }
    }

    #[allow(missing_docs)]
    pub fn broadcaster(&self) -> SBBroadcaster {
        SBBroadcaster::wrap(unsafe { sys::SBProcessGetBroadcaster(self.raw) })
    }

    /// Get an iterator over the [threads] known to this process instance.
    ///
    /// [threads]: struct.SBThread.html
    pub fn threads(&self) -> SBProcessThreadIter {
        SBProcessThreadIter {
            process: self,
            idx: 0,
        }
    }

    /// Returns the thread with the given thread ID.
    pub fn thread_by_id(&self, thread_id: lldb_tid_t) -> Option<SBThread> {
        SBThread::maybe_wrap(unsafe { sys::SBProcessGetThreadByID(self.raw, thread_id) })
    }

    /// Returns the thread with the given thread index ID.
    pub fn thread_by_index_id(&self, thread_index_id: u32) -> Option<SBThread> {
        SBThread::maybe_wrap(unsafe { sys::SBProcessGetThreadByIndexID(self.raw, thread_index_id) })
    }

    /// Returns the currently selected thread.
    pub fn selected_thread(&self) -> SBThread {
        SBThread::wrap(unsafe { sys::SBProcessGetSelectedThread(self.raw) })
    }

    /// Set the selected thread.
    pub fn set_selected_thread(&self, thread: &SBThread) -> bool {
        unsafe { sys::SBProcessSetSelectedThread(self.raw, thread.raw) != 0 }
    }

    /// Set the selected thread by ID.
    pub fn set_selected_thread_by_id(&self, thread_id: lldb_tid_t) -> bool {
        unsafe { sys::SBProcessSetSelectedThreadByID(self.raw, thread_id) != 0 }
    }

    /// Set the selected thread by index ID.
    pub fn set_selected_thread_by_index_id(&self, thread_index_id: u32) -> bool {
        unsafe { sys::SBProcessSetSelectedThreadByIndexID(self.raw, thread_index_id) != 0 }
    }

    #[allow(missing_docs)]
    pub fn event_as_process_event(event: &SBEvent) -> Option<SBProcessEvent> {
        if unsafe { sys::SBProcessEventIsProcessEvent(event.raw) != 0 } {
            Some(SBProcessEvent::new(event))
        } else {
            None
        }
    }

    /// Save the state of the process in a core file (or mini dump on Windows).
    pub fn save_core(&self, file_name: &str) -> Result<(), SBError> {
        let f = CString::new(file_name).unwrap();
        let error = SBError::wrap(unsafe { sys::SBProcessSaveCore(self.raw, f.as_ptr()) });
        if error.is_success() {
            Ok(())
        } else {
            Err(error)
        }
    }
}

/// Iterate over the [threads] in a [process].
///
/// [threads]: struct.SBThread.html
/// [process]: struct.SBProcess.html
pub struct SBProcessThreadIter<'d> {
    process: &'d SBProcess,
    idx: usize,
}

impl<'d> Iterator for SBProcessThreadIter<'d> {
    type Item = SBThread;

    fn next(&mut self) -> Option<SBThread> {
        if self.idx < unsafe { sys::SBProcessGetNumThreads(self.process.raw) as usize } {
            let r = Some(SBThread::wrap(unsafe {
                                            sys::SBProcessGetThreadAtIndex(self.process.raw,
                                                                           self.idx)
                                        }));
            self.idx += 1;
            r
        } else {
            None
        }
    }
}

impl fmt::Debug for SBProcess {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let stream = SBStream::new();
        unsafe { sys::SBProcessGetDescription(self.raw, stream.raw) };
        write!(fmt, "SBProcess {{ {} }}", stream.data())
    }
}

impl Drop for SBProcess {
    fn drop(&mut self) {
        unsafe { sys::DisposeSBProcess(self.raw) };
    }
}

#[allow(missing_docs)]
pub struct SBProcessEvent<'e> {
    event: &'e SBEvent,
}

#[allow(missing_docs)]
impl<'e> SBProcessEvent<'e> {
    pub fn new(event: &'e SBEvent) -> Self {
        SBProcessEvent { event: event }
    }

    pub fn process_state(&self) -> StateType {
        unsafe { sys::SBProcessGetStateFromEvent(self.event.raw) }
    }

    pub fn process(&self) -> SBProcess {
        SBProcess::wrap(unsafe { sys::SBProcessGetProcessFromEvent(self.event.raw) })
    }

    pub fn interrupted(&self) -> bool {
        unsafe { sys::SBProcessGetInterruptedFromEvent(self.event.raw) != 0 }
    }

    pub fn restarted(&self) -> bool {
        unsafe { sys::SBProcessGetRestartedFromEvent(self.event.raw) != 0 }
    }

    pub fn restarted_reasons(&self) -> SBProcessEventRestartedReasonIter {
        SBProcessEventRestartedReasonIter {
            event: self,
            idx: 0,
        }
    }
}

/// Iterate over the restart reasons in a [process event].
///
/// [process event]: struct.SBProcessEvent.html
pub struct SBProcessEventRestartedReasonIter<'d> {
    event: &'d SBProcessEvent<'d>,
    idx: usize,
}

impl<'d> Iterator for SBProcessEventRestartedReasonIter<'d> {
    type Item = &'d str;

    fn next(&mut self) -> Option<&'d str> {
        let raw = self.event.event.raw;
        if self.idx < unsafe { sys::SBProcessGetNumRestartedReasonsFromEvent(raw) as usize } {
            let r = unsafe {
                let s = CStr::from_ptr(sys::SBProcessGetRestartedReasonAtIndexFromEvent(raw,
                                                                                self.idx));
                match s.to_str() {
                    Ok(s) => s,
                    _ => panic!("Invalid string?"),
                }
            };
            self.idx += 1;
            Some(r)
        } else {
            None
        }
    }
}

#[cfg(feature = "graphql")]
graphql_object!(SBProcess: super::debugger::SBDebugger | &self | {
    field is_valid() -> bool {
        self.is_valid()
    }

    field is_alive() -> bool {
        self.is_alive()
    }

    field is_running() -> bool {
        self.is_running()
    }

    field is_stopped() -> bool {
        self.is_stopped()
    }

    // TODO(bm) This should be i32
    field exit_status() -> i64 {
        self.exit_status() as i64
    }

    field exit_description() -> &str {
        self.exit_description()
    }

    // TODO(bm): This should be u64
    field process_id() -> i64 {
        self.process_id() as i64
    }

    // TODO(bm) This should be u32
    field unique_id() -> i64 {
        self.unique_id() as i64
    }

    // TODO(bm) This should be u32
    field address_byte_size() -> i64 {
        self.address_byte_size() as i64
    }

    field threads() -> Vec<SBThread> {
        self.threads().collect()
    }

    field selected_thread() -> SBThread {
        self.selected_thread()
    }
});
<|endoftext|>


<commit_before>use command::Command;
use vm::VM;

pub struct Program {
    instructions: Vec<Command>,
    instruction_pointer: Option<usize>,
    current_depth: u64,
    pub status: ProgramStatus,
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ProgramStatus {
    Normal,
    Seeking(u64),
}

impl Program {
    pub fn new() -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Normal,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() {
            self.instruction_pointer = Some(0);
        }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        match (self.instruction_pointer, self.status) {
            (Some(mut index), ProgramStatus::Seeking(_)) => {
                self.handle_jump_forward(vm, &mut index);
                self.instruction_pointer = Some(index);
                if index < self.instructions.len() { self.execute(vm); }
            }

            (Some(mut index), ProgramStatus::Normal) => {
                while index < self.instructions.len() {
                    match self.instructions[index] {
                        Command::JumpForward => self.handle_jump_forward(vm, &mut index),
                        Command::JumpBackward => self.handle_jump_backward(vm, &mut index),
                        command => {
                            vm.apply(command);
                            index += 1;
                        }
                    }
                }

                self.instruction_pointer = Some(index);
            }

            _ => {}
        }
    }

    fn handle_jump_backward(&mut self, vm: &VM, index_ref: &mut usize) {
        match vm.cells[vm.data_pointer] {
            0 => {
                self.current_depth -= 1;
                *index_ref += 1;
            }

            _ => {
                let goal_depth = self.current_depth;
                for index in (0..*index_ref).rev() {
                    match self.instructions[index] {
                        Command::JumpBackward => self.current_depth += 1,
                        Command::JumpForward => {
                            if self.current_depth == goal_depth {
                                *index_ref = index + 1;
                                return;
                            }
                            self.current_depth -= 1;
                        }
                        _ => {}
                    }
                }
                panic!("No starting brace found!");
            }
        }
    }

    fn seek_forward(&mut self, index_ref: &mut usize) {
        if let ProgramStatus::Seeking(goal_depth) = self.status {
            for index in (*index_ref)..self.instructions.len() {
                match self.instructions[index] {
                    Command::JumpForward => self.current_depth += 1,
                    Command::JumpBackward => self.current_depth -= 1,
                    _ => {}
                }
                if self.current_depth == goal_depth {
                    *index_ref = index + 1;
                    self.status = ProgramStatus::Normal;
                    return;
                }
            }

            *index_ref = self.instructions.len();
            self.status = ProgramStatus::Seeking(goal_depth);
        }
    }

    fn handle_jump_forward(&mut self, vm: &VM, index: &mut usize) {
        match (vm.cells[vm.data_pointer], self.status) {
            (0, ProgramStatus::Normal) => {
                self.status = ProgramStatus::Seeking(self.current_depth);
                self.seek_forward(index);
            }

            (_, ProgramStatus::Normal) => {
                *index += 1;
                self.current_depth += 1;
                self.status = ProgramStatus::Normal;
            }

            (_, ProgramStatus::Seeking(_)) => self.seek_forward(index),

        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    pub fn test_seek_forward_normal() {
        let mut program = Program {
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Seeking(0),
            instructions: vec![
                Command::JumpForward,
                Command::JumpForward,
                Command::JumpBackward,
                Command::JumpBackward,
            ],
        };
        let mut index = 0;
        program.seek_forward(&mut index);
        assert_eq!(index, 4);
        assert_eq!(program.status, ProgramStatus::Normal);
    }

    #[test]
    pub fn test_seek_forward_seeking() {
        let mut program = Program {
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Seeking(0),
            instructions: vec![
                Command::JumpForward,
                Command::JumpForward,
                Command::JumpBackward,
            ],
        };
        let mut index = 0;
        program.seek_forward(&mut index);
        assert_eq!(index, 3);
        assert_eq!(program.status, ProgramStatus::Seeking(0));
    }
}
<commit_msg>Modify seek_forward function to be pure<commit_after>use command::Command;
use vm::VM;

pub struct Program {
    instructions: Vec<Command>,
    instruction_pointer: Option<usize>,
    current_depth: u64,
    pub status: ProgramStatus,
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ProgramStatus {
    Normal,
    Seeking(u64),
}

impl Program {
    pub fn new() -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Normal,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() {
            self.instruction_pointer = Some(0);
        }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        match (self.instruction_pointer, self.status) {
            (Some(mut index), ProgramStatus::Seeking(_)) => {
                self.handle_jump_forward(vm, &mut index);
                self.instruction_pointer = Some(index);
                if index < self.instructions.len() { self.execute(vm); }
            }

            (Some(mut index), ProgramStatus::Normal) => {
                while index < self.instructions.len() {
                    match self.instructions[index] {
                        Command::JumpForward => self.handle_jump_forward(vm, &mut index),
                        Command::JumpBackward => self.handle_jump_backward(vm, &mut index),
                        command => {
                            vm.apply(command);
                            index += 1;
                        }
                    }
                }

                self.instruction_pointer = Some(index);
            }

            _ => {}
        }
    }

    fn handle_jump_backward(&mut self, vm: &VM, index_ref: &mut usize) {
        match vm.cells[vm.data_pointer] {
            0 => {
                self.current_depth -= 1;
                *index_ref += 1;
            }

            _ => {
                let goal_depth = self.current_depth;
                for index in (0..*index_ref).rev() {
                    match self.instructions[index] {
                        Command::JumpBackward => self.current_depth += 1,
                        Command::JumpForward => {
                            if self.current_depth == goal_depth {
                                *index_ref = index + 1;
                                return;
                            }
                            self.current_depth -= 1;
                        }
                        _ => {}
                    }
                }
                panic!("No starting brace found!");
            }
        }
    }

    fn seek_forward(&self, starting_index: usize) -> (usize, ProgramStatus) {
        match self.status {
            ProgramStatus::Seeking(goal_depth) => {
                for index in starting_index..self.instructions.len() {
                    match self.instructions[index] {
                        Command::JumpForward => self.current_depth += 1,
                        Command::JumpBackward => self.current_depth -= 1,
                        _ => {}
                    }
                    if self.current_depth == goal_depth {
                        return (index + 1, ProgramStatus::Normal);
                    }
                }

                return (self.instructions.len(), ProgramStatus::Seeking(goal_depth));
            }

            ProgramStatus::Normal => return (starting_index, self.status)
        }
    }

    fn handle_jump_forward(&mut self, vm: &VM, index: &mut usize) {
        match (vm.cells[vm.data_pointer], self.status) {
            (0, ProgramStatus::Normal) => {
                self.status = ProgramStatus::Seeking(self.current_depth);
                self.seek_forward(index);
            }

            (_, ProgramStatus::Normal) => {
                *index += 1;
                self.current_depth += 1;
                self.status = ProgramStatus::Normal;
            }

            (_, ProgramStatus::Seeking(_)) => self.seek_forward(index),

        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    pub fn test_seek_forward_normal() {
        let mut program = Program {
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Seeking(0),
            instructions: vec![
                Command::JumpForward,
                Command::JumpForward,
                Command::JumpBackward,
                Command::JumpBackward,
            ],
        };
        let mut index = 0;
        program.seek_forward(&mut index);
        assert_eq!(index, 4);
        assert_eq!(program.status, ProgramStatus::Normal);
    }

    #[test]
    pub fn test_seek_forward_seeking() {
        let mut program = Program {
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Seeking(0),
            instructions: vec![
                Command::JumpForward,
                Command::JumpForward,
                Command::JumpBackward,
            ],
        };
        let mut index = 0;
        program.seek_forward(&mut index);
        assert_eq!(index, 3);
        assert_eq!(program.status, ProgramStatus::Seeking(0));
    }
}
<|endoftext|>


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

//! Reorder items.
//!
//! `mod`, `extern crate` and `use` declarations are reorderd in alphabetical
//! order. Trait items are reordered in pre-determined order (associated types
//! and constatns comes before methods).

// TODO(#2455): Reorder trait items.

use config::{Config, lists::*};
use syntax::{ast, attr, codemap::Span};

use attr::filter_inline_attrs;
use codemap::LineRangeUtils;
use comment::combine_strs_with_missing_comments;
use imports::{path_to_imported_ident, rewrite_import};
use items::{rewrite_extern_crate, rewrite_mod};
use lists::{itemize_list, write_list, ListFormatting};
use rewrite::{Rewrite, RewriteContext};
use shape::Shape;
use spanned::Spanned;
use utils::mk_sp;
use visitor::FmtVisitor;

use std::cmp::Ordering;

fn compare_path_segments(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering {
    a.identifier.name.as_str().cmp(&b.identifier.name.as_str())
}

fn compare_paths(a: &ast::Path, b: &ast::Path) -> Ordering {
    for segment in a.segments.iter().zip(b.segments.iter()) {
        let ord = compare_path_segments(segment.0, segment.1);
        if ord != Ordering::Equal {
            return ord;
        }
    }
    a.segments.len().cmp(&b.segments.len())
}

fn compare_use_trees(a: &ast::UseTree, b: &ast::UseTree, nested: bool) -> Ordering {
    use ast::UseTreeKind::*;

    // `use_nested_groups` is not yet supported, remove the `if !nested` when support will be
    // fully added
    if !nested {
        let paths_cmp = compare_paths(&a.prefix, &b.prefix);
        if paths_cmp != Ordering::Equal {
            return paths_cmp;
        }
    }

    match (&a.kind, &b.kind) {
        (&Simple(ident_a), &Simple(ident_b)) => {
            let name_a = &*path_to_imported_ident(&a.prefix).name.as_str();
            let name_b = &*path_to_imported_ident(&b.prefix).name.as_str();
            let name_ordering = if name_a == "self" {
                if name_b == "self" {
                    Ordering::Equal
                } else {
                    Ordering::Less
                }
            } else if name_b == "self" {
                Ordering::Greater
            } else {
                name_a.cmp(name_b)
            };
            if name_ordering == Ordering::Equal {
                if ident_a.name.as_str() != name_a {
                    if ident_b.name.as_str() != name_b {
                        ident_a.name.as_str().cmp(&ident_b.name.as_str())
                    } else {
                        Ordering::Greater
                    }
                } else {
                    Ordering::Less
                }
            } else {
                name_ordering
            }
        }
        (&Glob, &Glob) => Ordering::Equal,
        (&Simple(_), _) | (&Glob, &Nested(_)) => Ordering::Less,
        (&Nested(ref a_items), &Nested(ref b_items)) => {
            let mut a = a_items
                .iter()
                .map(|&(ref tree, _)| tree.clone())
                .collect::<Vec<_>>();
            let mut b = b_items
                .iter()
                .map(|&(ref tree, _)| tree.clone())
                .collect::<Vec<_>>();
            a.sort_by(|a, b| compare_use_trees(a, b, true));
            b.sort_by(|a, b| compare_use_trees(a, b, true));
            for comparison_pair in a.iter().zip(b.iter()) {
                let ord = compare_use_trees(comparison_pair.0, comparison_pair.1, true);
                if ord != Ordering::Equal {
                    return ord;
                }
            }
            a.len().cmp(&b.len())
        }
        (&Glob, &Simple(_)) | (&Nested(_), _) => Ordering::Greater,
    }
}

/// Choose the ordering between the given two items.
fn compare_items(a: &ast::Item, b: &ast::Item) -> Ordering {
    match (&a.node, &b.node) {
        (&ast::ItemKind::Mod(..), &ast::ItemKind::Mod(..)) => {
            a.ident.name.as_str().cmp(&b.ident.name.as_str())
        }
        (&ast::ItemKind::Use(ref a_tree), &ast::ItemKind::Use(ref b_tree)) => {
            compare_use_trees(a_tree, b_tree, false)
        }
        (&ast::ItemKind::ExternCrate(ref a_name), &ast::ItemKind::ExternCrate(ref b_name)) => {
            // `extern crate foo as bar;`
            //               ^^^ Comparing this.
            let a_orig_name =
                a_name.map_or_else(|| a.ident.name.as_str(), |symbol| symbol.as_str());
            let b_orig_name =
                b_name.map_or_else(|| b.ident.name.as_str(), |symbol| symbol.as_str());
            let result = a_orig_name.cmp(&b_orig_name);
            if result != Ordering::Equal {
                return result;
            }

            // `extern crate foo as bar;`
            //                      ^^^ Comparing this.
            match (a_name, b_name) {
                (Some(..), None) => Ordering::Greater,
                (None, Some(..)) => Ordering::Less,
                (None, None) => Ordering::Equal,
                (Some(..), Some(..)) => a.ident.name.as_str().cmp(&b.ident.name.as_str()),
            }
        }
        _ => unreachable!(),
    }
}

/// Rewrite a list of items with reordering. Every item in `items` must have
/// the same `ast::ItemKind`.
// TODO (some day) remove unused imports, expand globs, compress many single
// imports into a list import.
fn rewrite_reorderable_items(
    context: &RewriteContext,
    reorderable_items: &[&ast::Item],
    shape: Shape,
    span: Span,
) -> Option<String> {
    let items = itemize_list(
        context.snippet_provider,
        reorderable_items.iter(),
        "",
        ";",
        |item| item.span().lo(),
        |item| item.span().hi(),
        |item| {
            let attrs = filter_inline_attrs(&item.attrs, item.span());
            let attrs_str = attrs.rewrite(context, shape)?;

            let missed_span = if attrs.is_empty() {
                mk_sp(item.span.lo(), item.span.lo())
            } else {
                mk_sp(attrs.last().unwrap().span.hi(), item.span.lo())
            };

            let item_str = match item.node {
                ast::ItemKind::Use(ref tree) => {
                    rewrite_import(context, &item.vis, tree, &item.attrs, shape)?
                }
                ast::ItemKind::ExternCrate(..) => rewrite_extern_crate(context, item)?,
                ast::ItemKind::Mod(..) => rewrite_mod(item),
                _ => return None,
            };

            combine_strs_with_missing_comments(
                context,
                &attrs_str,
                &item_str,
                missed_span,
                shape,
                false,
            )
        },
        span.lo(),
        span.hi(),
        false,
    );
    let mut item_pair_vec: Vec<_> = items.zip(reorderable_items.iter()).collect();
    item_pair_vec.sort_by(|a, b| compare_items(a.1, b.1));
    let item_vec: Vec<_> = item_pair_vec.into_iter().map(|pair| pair.0).collect();

    let fmt = ListFormatting {
        tactic: DefinitiveListTactic::Vertical,
        separator: "",
        trailing_separator: SeparatorTactic::Never,
        separator_place: SeparatorPlace::Back,
        shape,
        ends_with_newline: true,
        preserve_newline: false,
        config: context.config,
    };

    write_list(&item_vec, &fmt)
}

fn contains_macro_use_attr(item: &ast::Item) -> bool {
    attr::contains_name(&filter_inline_attrs(&item.attrs, item.span()), "macro_use")
}

/// A simplified version of `ast::ItemKind`.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
enum ReorderableItemKind {
    ExternCrate,
    Mod,
    Use,
    /// An item that cannot be reordered. Either has an unreorderable item kind
    /// or an `macro_use` attribute.
    Other,
}

impl ReorderableItemKind {
    pub fn from(item: &ast::Item) -> Self {
        match item.node {
            _ if contains_macro_use_attr(item) => ReorderableItemKind::Other,
            ast::ItemKind::ExternCrate(..) => ReorderableItemKind::ExternCrate,
            ast::ItemKind::Mod(..) => ReorderableItemKind::Mod,
            ast::ItemKind::Use(..) => ReorderableItemKind::Use,
            _ => ReorderableItemKind::Other,
        }
    }

    pub fn is_same_item_kind(&self, item: &ast::Item) -> bool {
        ReorderableItemKind::from(item) == *self
    }

    pub fn is_reorderable(&self, config: &Config) -> bool {
        match *self {
            ReorderableItemKind::ExternCrate => config.reorder_extern_crates(),
            ReorderableItemKind::Mod => config.reorder_modules(),
            ReorderableItemKind::Use => config.reorder_imports(),
            ReorderableItemKind::Other => false,
        }
    }

    pub fn in_group(&self, config: &Config) -> bool {
        match *self {
            ReorderableItemKind::ExternCrate => config.reorder_extern_crates_in_group(),
            ReorderableItemKind::Mod => config.reorder_modules(),
            ReorderableItemKind::Use => config.reorder_imports_in_group(),
            ReorderableItemKind::Other => false,
        }
    }
}

impl<'b, 'a: 'b> FmtVisitor<'a> {
    /// Format items with the same item kind and reorder them. If `in_group` is
    /// `true`, then the items separated by an empty line will not be reordered
    /// together.
    fn walk_reorderable_items(
        &mut self,
        items: &[&ast::Item],
        item_kind: ReorderableItemKind,
        in_group: bool,
    ) -> usize {
        let mut last = self.codemap.lookup_line_range(items[0].span());
        let item_length = items
            .iter()
            .take_while(|ppi| {
                item_kind.is_same_item_kind(&***ppi) && (!in_group || {
                    let current = self.codemap.lookup_line_range(ppi.span());
                    let in_same_group = current.lo < last.hi + 2;
                    last = current;
                    in_same_group
                })
            })
            .count();
        let items = &items[..item_length];

        let at_least_one_in_file_lines = items
            .iter()
            .any(|item| !out_of_file_lines_range!(self, item.span));

        if at_least_one_in_file_lines && !items.is_empty() {
            let lo = items.first().unwrap().span().lo();
            let hi = items.last().unwrap().span().hi();
            let span = mk_sp(lo, hi);
            let rw = rewrite_reorderable_items(&self.get_context(), items, self.shape(), span);
            self.push_rewrite(span, rw);
        } else {
            for item in items {
                self.push_rewrite(item.span, None);
            }
        }

        item_length
    }

    /// Visit and format the given items. Items are reordered If they are
    /// consecutive and reorderable.
    pub fn visit_items_with_reordering(&mut self, mut items: &[&ast::Item]) {
        while !items.is_empty() {
            // If the next item is a `use`, `extern crate` or `mod`, then extract it and any
            // subsequent items that have the same item kind to be reordered within
            // `walk_reorderable_items`. Otherwise, just format the next item for output.
            let item_kind = ReorderableItemKind::from(items[0]);
            if item_kind.is_reorderable(self.config) {
                let visited_items_num =
                    self.walk_reorderable_items(items, item_kind, item_kind.in_group(self.config));
                let (_, rest) = items.split_at(visited_items_num);
                items = rest;
            } else {
                // Reaching here means items were not reordered. There must be at least
                // one item left in `items`, so calling `unwrap()` here is safe.
                let (item, rest) = items.split_first().unwrap();
                self.visit_item(item);
                items = rest;
            }
        }
    }
}
<commit_msg>Do not reorder inline modules<commit_after>// Copyright 2018 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.

//! Reorder items.
//!
//! `mod`, `extern crate` and `use` declarations are reorderd in alphabetical
//! order. Trait items are reordered in pre-determined order (associated types
//! and constatns comes before methods).

// TODO(#2455): Reorder trait items.

use config::{Config, lists::*};
use syntax::{ast, attr, codemap::Span};

use attr::filter_inline_attrs;
use codemap::LineRangeUtils;
use comment::combine_strs_with_missing_comments;
use imports::{path_to_imported_ident, rewrite_import};
use items::{is_mod_decl, rewrite_extern_crate, rewrite_mod};
use lists::{itemize_list, write_list, ListFormatting};
use rewrite::{Rewrite, RewriteContext};
use shape::Shape;
use spanned::Spanned;
use utils::mk_sp;
use visitor::FmtVisitor;

use std::cmp::Ordering;

fn compare_path_segments(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering {
    a.identifier.name.as_str().cmp(&b.identifier.name.as_str())
}

fn compare_paths(a: &ast::Path, b: &ast::Path) -> Ordering {
    for segment in a.segments.iter().zip(b.segments.iter()) {
        let ord = compare_path_segments(segment.0, segment.1);
        if ord != Ordering::Equal {
            return ord;
        }
    }
    a.segments.len().cmp(&b.segments.len())
}

fn compare_use_trees(a: &ast::UseTree, b: &ast::UseTree, nested: bool) -> Ordering {
    use ast::UseTreeKind::*;

    // `use_nested_groups` is not yet supported, remove the `if !nested` when support will be
    // fully added
    if !nested {
        let paths_cmp = compare_paths(&a.prefix, &b.prefix);
        if paths_cmp != Ordering::Equal {
            return paths_cmp;
        }
    }

    match (&a.kind, &b.kind) {
        (&Simple(ident_a), &Simple(ident_b)) => {
            let name_a = &*path_to_imported_ident(&a.prefix).name.as_str();
            let name_b = &*path_to_imported_ident(&b.prefix).name.as_str();
            let name_ordering = if name_a == "self" {
                if name_b == "self" {
                    Ordering::Equal
                } else {
                    Ordering::Less
                }
            } else if name_b == "self" {
                Ordering::Greater
            } else {
                name_a.cmp(name_b)
            };
            if name_ordering == Ordering::Equal {
                if ident_a.name.as_str() != name_a {
                    if ident_b.name.as_str() != name_b {
                        ident_a.name.as_str().cmp(&ident_b.name.as_str())
                    } else {
                        Ordering::Greater
                    }
                } else {
                    Ordering::Less
                }
            } else {
                name_ordering
            }
        }
        (&Glob, &Glob) => Ordering::Equal,
        (&Simple(_), _) | (&Glob, &Nested(_)) => Ordering::Less,
        (&Nested(ref a_items), &Nested(ref b_items)) => {
            let mut a = a_items
                .iter()
                .map(|&(ref tree, _)| tree.clone())
                .collect::<Vec<_>>();
            let mut b = b_items
                .iter()
                .map(|&(ref tree, _)| tree.clone())
                .collect::<Vec<_>>();
            a.sort_by(|a, b| compare_use_trees(a, b, true));
            b.sort_by(|a, b| compare_use_trees(a, b, true));
            for comparison_pair in a.iter().zip(b.iter()) {
                let ord = compare_use_trees(comparison_pair.0, comparison_pair.1, true);
                if ord != Ordering::Equal {
                    return ord;
                }
            }
            a.len().cmp(&b.len())
        }
        (&Glob, &Simple(_)) | (&Nested(_), _) => Ordering::Greater,
    }
}

/// Choose the ordering between the given two items.
fn compare_items(a: &ast::Item, b: &ast::Item) -> Ordering {
    match (&a.node, &b.node) {
        (&ast::ItemKind::Mod(..), &ast::ItemKind::Mod(..)) => {
            a.ident.name.as_str().cmp(&b.ident.name.as_str())
        }
        (&ast::ItemKind::Use(ref a_tree), &ast::ItemKind::Use(ref b_tree)) => {
            compare_use_trees(a_tree, b_tree, false)
        }
        (&ast::ItemKind::ExternCrate(ref a_name), &ast::ItemKind::ExternCrate(ref b_name)) => {
            // `extern crate foo as bar;`
            //               ^^^ Comparing this.
            let a_orig_name =
                a_name.map_or_else(|| a.ident.name.as_str(), |symbol| symbol.as_str());
            let b_orig_name =
                b_name.map_or_else(|| b.ident.name.as_str(), |symbol| symbol.as_str());
            let result = a_orig_name.cmp(&b_orig_name);
            if result != Ordering::Equal {
                return result;
            }

            // `extern crate foo as bar;`
            //                      ^^^ Comparing this.
            match (a_name, b_name) {
                (Some(..), None) => Ordering::Greater,
                (None, Some(..)) => Ordering::Less,
                (None, None) => Ordering::Equal,
                (Some(..), Some(..)) => a.ident.name.as_str().cmp(&b.ident.name.as_str()),
            }
        }
        _ => unreachable!(),
    }
}

/// Rewrite a list of items with reordering. Every item in `items` must have
/// the same `ast::ItemKind`.
// TODO (some day) remove unused imports, expand globs, compress many single
// imports into a list import.
fn rewrite_reorderable_items(
    context: &RewriteContext,
    reorderable_items: &[&ast::Item],
    shape: Shape,
    span: Span,
) -> Option<String> {
    let items = itemize_list(
        context.snippet_provider,
        reorderable_items.iter(),
        "",
        ";",
        |item| item.span().lo(),
        |item| item.span().hi(),
        |item| {
            let attrs = filter_inline_attrs(&item.attrs, item.span());
            let attrs_str = attrs.rewrite(context, shape)?;

            let missed_span = if attrs.is_empty() {
                mk_sp(item.span.lo(), item.span.lo())
            } else {
                mk_sp(attrs.last().unwrap().span.hi(), item.span.lo())
            };

            let item_str = match item.node {
                ast::ItemKind::Use(ref tree) => {
                    rewrite_import(context, &item.vis, tree, &item.attrs, shape)?
                }
                ast::ItemKind::ExternCrate(..) => rewrite_extern_crate(context, item)?,
                ast::ItemKind::Mod(..) => rewrite_mod(item),
                _ => return None,
            };

            combine_strs_with_missing_comments(
                context,
                &attrs_str,
                &item_str,
                missed_span,
                shape,
                false,
            )
        },
        span.lo(),
        span.hi(),
        false,
    );
    let mut item_pair_vec: Vec<_> = items.zip(reorderable_items.iter()).collect();
    item_pair_vec.sort_by(|a, b| compare_items(a.1, b.1));
    let item_vec: Vec<_> = item_pair_vec.into_iter().map(|pair| pair.0).collect();

    let fmt = ListFormatting {
        tactic: DefinitiveListTactic::Vertical,
        separator: "",
        trailing_separator: SeparatorTactic::Never,
        separator_place: SeparatorPlace::Back,
        shape,
        ends_with_newline: true,
        preserve_newline: false,
        config: context.config,
    };

    write_list(&item_vec, &fmt)
}

fn contains_macro_use_attr(item: &ast::Item) -> bool {
    attr::contains_name(&filter_inline_attrs(&item.attrs, item.span()), "macro_use")
}

/// A simplified version of `ast::ItemKind`.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
enum ReorderableItemKind {
    ExternCrate,
    Mod,
    Use,
    /// An item that cannot be reordered. Either has an unreorderable item kind
    /// or an `macro_use` attribute.
    Other,
}

impl ReorderableItemKind {
    pub fn from(item: &ast::Item) -> Self {
        match item.node {
            _ if contains_macro_use_attr(item) => ReorderableItemKind::Other,
            ast::ItemKind::ExternCrate(..) => ReorderableItemKind::ExternCrate,
            ast::ItemKind::Mod(..) if is_mod_decl(item) => ReorderableItemKind::Mod,
            ast::ItemKind::Use(..) => ReorderableItemKind::Use,
            _ => ReorderableItemKind::Other,
        }
    }

    pub fn is_same_item_kind(&self, item: &ast::Item) -> bool {
        ReorderableItemKind::from(item) == *self
    }

    pub fn is_reorderable(&self, config: &Config) -> bool {
        match *self {
            ReorderableItemKind::ExternCrate => config.reorder_extern_crates(),
            ReorderableItemKind::Mod => config.reorder_modules(),
            ReorderableItemKind::Use => config.reorder_imports(),
            ReorderableItemKind::Other => false,
        }
    }

    pub fn in_group(&self, config: &Config) -> bool {
        match *self {
            ReorderableItemKind::ExternCrate => config.reorder_extern_crates_in_group(),
            ReorderableItemKind::Mod => config.reorder_modules(),
            ReorderableItemKind::Use => config.reorder_imports_in_group(),
            ReorderableItemKind::Other => false,
        }
    }
}

impl<'b, 'a: 'b> FmtVisitor<'a> {
    /// Format items with the same item kind and reorder them. If `in_group` is
    /// `true`, then the items separated by an empty line will not be reordered
    /// together.
    fn walk_reorderable_items(
        &mut self,
        items: &[&ast::Item],
        item_kind: ReorderableItemKind,
        in_group: bool,
    ) -> usize {
        let mut last = self.codemap.lookup_line_range(items[0].span());
        let item_length = items
            .iter()
            .take_while(|ppi| {
                item_kind.is_same_item_kind(&***ppi) && (!in_group || {
                    let current = self.codemap.lookup_line_range(ppi.span());
                    let in_same_group = current.lo < last.hi + 2;
                    last = current;
                    in_same_group
                })
            })
            .count();
        let items = &items[..item_length];

        let at_least_one_in_file_lines = items
            .iter()
            .any(|item| !out_of_file_lines_range!(self, item.span));

        if at_least_one_in_file_lines && !items.is_empty() {
            let lo = items.first().unwrap().span().lo();
            let hi = items.last().unwrap().span().hi();
            let span = mk_sp(lo, hi);
            let rw = rewrite_reorderable_items(&self.get_context(), items, self.shape(), span);
            self.push_rewrite(span, rw);
        } else {
            for item in items {
                self.push_rewrite(item.span, None);
            }
        }

        item_length
    }

    /// Visit and format the given items. Items are reordered If they are
    /// consecutive and reorderable.
    pub fn visit_items_with_reordering(&mut self, mut items: &[&ast::Item]) {
        while !items.is_empty() {
            // If the next item is a `use`, `extern crate` or `mod`, then extract it and any
            // subsequent items that have the same item kind to be reordered within
            // `walk_reorderable_items`. Otherwise, just format the next item for output.
            let item_kind = ReorderableItemKind::from(items[0]);
            if item_kind.is_reorderable(self.config) {
                let visited_items_num =
                    self.walk_reorderable_items(items, item_kind, item_kind.in_group(self.config));
                let (_, rest) = items.split_at(visited_items_num);
                items = rest;
            } else {
                // Reaching here means items were not reordered. There must be at least
                // one item left in `items`, so calling `unwrap()` here is safe.
                let (item, rest) = items.split_first().unwrap();
                self.visit_item(item);
                items = rest;
            }
        }
    }
}
<|endoftext|>


<commit_before>//! This module contains the logic used to resolve identifiers to their locals or globals.
//!
//! Internally, this module also performs the register allocation for all locals. This makes it
//! easy to reference upvalues (see `UpvalDesc` in program.rs) and makes the bytecode emitter do
//! less work.

use ast::*;
use visit::*;
use program::UpvalDesc;
use span::{Span, Spanned};

use std::mem;
use std::collections::HashMap;
use std::default::Default;

/// Data used by the resolver, associated to a function
struct FuncData {
    locals: Vec<String>,

    /// Upvalues referenced from within the function
    upvals: Vec<UpvalDesc>,
    upval_names: Vec<String>,
    /// Maps known upvalue names to their id
    upval_map: HashMap<String, usize>,

    /// Stack of lists of locals. Tracks all active scopes and thus all available locals.
    scopes: Vec<Vec<usize>>,
}

impl FuncData {
    fn new(locals: Vec<String>) -> FuncData {
        FuncData {
            locals: locals,
            upvals: vec![],
            upval_names: vec![],
            upval_map: Default::default(),
            scopes: vec![],
        }
    }

    /// Registers an upvalue and returns its id
    fn add_upval(&mut self, name: String, desc: UpvalDesc) -> usize {
        let id = self.upvals.len();
        self.upvals.push(desc);
        self.upval_names.push(name.clone());
        self.upval_map.insert(name, id);

        id
    }

    /// Finds a reachable local (no upvalues are considered) with the given name and returns its id
    fn get_local(&self, name: &String) -> Option<usize> {
        // scan backwards through scopes
        let mut level = self.scopes.len() - 1;
        loop {
            for id in &self.scopes[level] {
                if *name == self.locals[*id] {
                    return Some(*id);
                }
            }

            if level == 0 { break; }
            level -= 1;
        }

        None
    }
}

/// A resolver will resolve any `VNamed` references in a function
struct Resolver {
    /// Stack of active functions
    funcs: Vec<FuncData>,
}

impl Resolver {
    /// Finds an upvalue in a parent function
    fn find_upval(&mut self, name: &String, userlvl: usize) -> Option<usize> {
        if userlvl == 0 {
            return None;
        }

        // search parent functions for locals / upvalues that match
        if let Some(id) = self.funcs[userlvl - 1].get_local(name) {
            return Some(self.funcs[userlvl].add_upval(name.clone(), UpvalDesc::Local(id)));
        } else {
            if let Some(id) = self.find_upval(name, userlvl - 1) {
                return Some(self.funcs[userlvl].add_upval(name.clone(), UpvalDesc::Upval(id)));
            } else {
                return None;
            }
        }

        None
    }

    /// Searches for an upvalue with the given name
    fn get_upval(&mut self, name: &String) -> Option<usize> {
        let level = self.funcs.len() - 1;

        {
            // search known upvals first
            let data = &self.funcs[level];
            if let Some(id) = data.upval_map.get(name) {
                return Some(*id);
            }
        }

        self.find_upval(name, level)
    }

    /// Declares a new local with the given name in the currently active scope and function.
    fn add_local(&mut self, name: String) -> usize {
        let level = self.funcs.len() - 1;

        // this might be a redeclaration, in which case we ignore it (the emitter handles it)
        let func = &mut self.funcs[level];
        let scopelvl = func.scopes.len() - 1;

        {
            let scope = &func.scopes[scopelvl];
            for id in scope {
                let lname = &func.locals[*id];
                if *lname == name {
                    // already declared
                    return *id;
                }
            }
        }

        // create new local
        let scope = &mut func.scopes[scopelvl];
        let id = func.locals.len();
        func.locals.push(name);
        scope.push(id);

        id
    }

    /// Resolves a block and declares a list of locals inside of it
    fn resolve_block(&mut self, b: &mut Block, locals: Vec<String>) {
        let level = self.funcs.len() - 1;
        self.funcs[level].scopes.push(vec![]);
        for name in locals {
            self.add_local(name);
        }

        walk_block(b, self);

        let data = &mut self.funcs[level];
        let scope = data.scopes.pop().unwrap();
        for id in scope {
            let name = &data.locals[id];
            b.localmap.insert(name.clone(), id);
        }
    }

    /// Resolves the given named variable
    fn resolve_var(&mut self, name: &String, span: Span) -> _Variable {
        // first, try a local with that name
        if let Some(id) = self.funcs[self.funcs.len() - 1].get_local(&name) {
            VLocal(id)
        } else {
            // find an upvalue
            if let Some(id) = self.get_upval(&name) {
                VUpval(id)
            } else {
                // fall back to global access; resolve environment
                let envvar = Box::new(Spanned::new(span,
                    self.resolve_var(&"_ENV".to_string(), span)));

                VResGlobal(envvar, name.clone())
            }
        }
    }
}

impl Visitor for Resolver {
    fn visit_stmt(&mut self, s: &mut Stmt) {
        match s.value {
            SDecl(ref mut names, ref mut exprs) => {
                for expr in exprs {
                    walk_expr(expr, self);
                }

                for name in names {
                    self.add_local(name.clone());
                }
            },
            SLFunc(ref mut name, ref mut f) => {
                self.add_local(name.clone());
                self.visit_func(f);
            },
            SFor{ref var, ref mut body, ..} => {
                self.resolve_block(body, vec![var.clone()]);
            },
            SForIn{ref vars, ref mut body, ..} => {
                self.resolve_block(body, vars.clone());
            },
            _ => {
                walk_stmt(s, self);
            }
        }
    }

    fn visit_var(&mut self, v: &mut Variable) {
        {
            let ref mut var = v.value;

            if let VNamed(..) = *var {
                let newvar = if let VNamed(ref name) = *var {
                    self.resolve_var(name, v.span)
                } else { unreachable!(); };

                mem::replace(var, newvar);
                return;
            }
        }

        walk_var(v, self);
    }

    fn visit_block(&mut self, b: &mut Block) {
        self.resolve_block(b, vec![]);
    }

    fn visit_func(&mut self, f: &mut Function) {
        let mut data = FuncData::new(f.params.clone());
        if self.funcs.len() == 0 {
            // root function, add implicit _ENV upvalue
            data.add_upval("_ENV".to_string(), UpvalDesc::Upval(0));    // the UpvalDesc is ignored
        }

        self.funcs.push(data);
        self.visit_block(&mut f.value.body);

        let data = self.funcs.pop().unwrap();
        f.locals = data.locals;
        f.upvalues = data.upvals;
    }
}

/// Resolves all locals used in the given block. Recursively resolves all blocks found inside.
///
/// Allows blocks inside the given block to access locals declared within the parent block
/// (assuming they are declared before the block). Does not allow the given block to access outer
/// locals.
///
/// This also resolves any `VGlobal` to `VResGlobal` and resolves the function environments.
pub fn resolve_func(f: &mut Function) {
    Resolver {
        funcs: vec![],
    }.visit_func(f);
}

#[cfg(test)]
mod tests {
    use super::*;
    use parser::parse_main;
    use span::Spanned;
    use program::UpvalDesc;
    use ast::*;

    use std::default::Default;

    macro_rules! localmap {
        () => {{
            ::std::collections::HashMap::<String, usize>::new()
        }};
        ( $($key:ident: $e:expr),* ) => {{
            let mut m = ::std::collections::HashMap::<String, usize>::new();
            $( m.insert(stringify!($key).to_string(), $e); )*

            m
        }};
    }

    #[test]
    fn simple() {
        let mut f = parse_main(r#"
i = 0
local a
do
    local i
    local j = i
    i[j] = a
end
j = i
"#).unwrap();
        resolve_func(&mut f);

        assert_eq!(f.value.body, Block::with_locals(vec![
            Spanned::default(SAssign(
                vec![Spanned::default(VResGlobal(Box::new(Spanned::default(VUpval(0))), "i".to_string()))],
                vec![Spanned::default(ELit(TInt(0)))],
            )),
            Spanned::default(SDecl(vec!["a".to_string()], vec![])),
            Spanned::default(SDo(Block::with_locals(vec![
                Spanned::default(SDecl(vec!["i".to_string()], vec![])),
                Spanned::default(SDecl(vec!["j".to_string()], vec![
                    Spanned::default(EVar(Spanned::default(VLocal(1)))),
                ])),
                Spanned::default(SAssign(
                    vec![Spanned::default(VIndex(
                        Box::new(Spanned::default(VLocal(1))),
                        Box::new(Spanned::default(EVar(Spanned::default(VLocal(2)))))
                    ))],
                    vec![Spanned::default(EVar(Spanned::default(VLocal(0))))],
                )),
            ], Default::default(), localmap!{ i: 1, j: 2 }))),
            Spanned::default(SAssign(
                vec![Spanned::default(
                    VResGlobal(Box::new(Spanned::default(VUpval(0))), "j".to_string())
                )],
                vec![Spanned::default(EVar(Spanned::default(
                    VResGlobal(Box::new(Spanned::default(VUpval(0))), "i".to_string())
                )))],
            )),
        ], Default::default(), localmap!{ a: 0 }));
    }

    #[test]
    fn complex() {
        let mut f = parse_main(r#"
local a
local function f()
    f = nil     // upvalue
    local f = f // both

    local function g() f = a end    // chained upvalue + normal upvalue
end
"#).unwrap();
        resolve_func(&mut f);

        assert_eq!(f.value.body, Block::with_locals(vec![
            Spanned::default(SDecl(vec!["a".to_string()], vec![])),
            Spanned::default(SLFunc("f".to_string(), Spanned::default(_Function {
                params: vec![],
                varargs: false,
                locals: vec!["f".to_string(), "g".to_string()],
                upvalues: vec![UpvalDesc::Local(1), UpvalDesc::Local(0)],
                body: Block::with_locals(vec![
                    Spanned::default(SAssign(vec![
                        Spanned::default(VUpval(0))
                    ], vec![
                        Spanned::default(ELit(TNil))
                    ])),
                    Spanned::default(SDecl(vec!["f".to_string()], vec![
                        Spanned::default(EVar(Spanned::default(VUpval(0))))
                    ])),
                    Spanned::default(SLFunc("g".to_string(), Spanned::default(_Function {
                        params: vec![],
                        varargs: false,
                        locals: vec![],
                        upvalues: vec![UpvalDesc::Local(0), UpvalDesc::Upval(1)],
                        body: Block::with_locals(vec![
                            Spanned::default(SAssign(vec![
                                Spanned::default(VUpval(0))
                            ], vec![
                                Spanned::default(EVar(Spanned::default(VUpval(1))))
                            ])),
                        ], Default::default(), localmap!{}),
                    }))),
                ], Default::default(), localmap!{ f: 0, g: 1 }),
            }))),
        ], Default::default(), localmap!{ a: 0, f: 1 }));
    }
}
<commit_msg>Added test for global resolution<commit_after>//! This module contains the logic used to resolve identifiers to their locals or globals.
//!
//! Internally, this module also performs the register allocation for all locals. This makes it
//! easy to reference upvalues (see `UpvalDesc` in program.rs) and makes the bytecode emitter do
//! less work.

use ast::*;
use visit::*;
use program::UpvalDesc;
use span::{Span, Spanned};

use std::mem;
use std::collections::HashMap;
use std::default::Default;

/// Data used by the resolver, associated to a function
struct FuncData {
    locals: Vec<String>,

    /// Upvalues referenced from within the function
    upvals: Vec<UpvalDesc>,
    upval_names: Vec<String>,
    /// Maps known upvalue names to their id
    upval_map: HashMap<String, usize>,

    /// Stack of lists of locals. Tracks all active scopes and thus all available locals.
    scopes: Vec<Vec<usize>>,
}

impl FuncData {
    fn new(locals: Vec<String>) -> FuncData {
        FuncData {
            locals: locals,
            upvals: vec![],
            upval_names: vec![],
            upval_map: Default::default(),
            scopes: vec![],
        }
    }

    /// Registers an upvalue and returns its id
    fn add_upval(&mut self, name: String, desc: UpvalDesc) -> usize {
        let id = self.upvals.len();
        self.upvals.push(desc);
        self.upval_names.push(name.clone());
        self.upval_map.insert(name, id);

        id
    }

    /// Finds a reachable local (no upvalues are considered) with the given name and returns its id
    fn get_local(&self, name: &String) -> Option<usize> {
        // scan backwards through scopes
        let mut level = self.scopes.len() - 1;
        loop {
            for id in &self.scopes[level] {
                if *name == self.locals[*id] {
                    return Some(*id);
                }
            }

            if level == 0 { break; }
            level -= 1;
        }

        None
    }
}

/// A resolver will resolve any `VNamed` references in a function
struct Resolver {
    /// Stack of active functions
    funcs: Vec<FuncData>,
}

impl Resolver {
    /// Finds an upvalue in a parent function
    fn find_upval(&mut self, name: &String, userlvl: usize) -> Option<usize> {
        if userlvl == 0 {
            return None;
        }

        // search parent functions for locals / upvalues that match
        if let Some(id) = self.funcs[userlvl - 1].get_local(name) {
            return Some(self.funcs[userlvl].add_upval(name.clone(), UpvalDesc::Local(id)));
        } else {
            if let Some(id) = self.find_upval(name, userlvl - 1) {
                return Some(self.funcs[userlvl].add_upval(name.clone(), UpvalDesc::Upval(id)));
            } else {
                return None;
            }
        }

        None
    }

    /// Searches for an upvalue with the given name
    fn get_upval(&mut self, name: &String) -> Option<usize> {
        let level = self.funcs.len() - 1;

        {
            // search known upvals first
            let data = &self.funcs[level];
            if let Some(id) = data.upval_map.get(name) {
                return Some(*id);
            }
        }

        self.find_upval(name, level)
    }

    /// Declares a new local with the given name in the currently active scope and function.
    fn add_local(&mut self, name: String) -> usize {
        let level = self.funcs.len() - 1;

        // this might be a redeclaration, in which case we ignore it (the emitter handles it)
        let func = &mut self.funcs[level];
        let scopelvl = func.scopes.len() - 1;

        {
            let scope = &func.scopes[scopelvl];
            for id in scope {
                let lname = &func.locals[*id];
                if *lname == name {
                    // already declared
                    return *id;
                }
            }
        }

        // create new local
        let scope = &mut func.scopes[scopelvl];
        let id = func.locals.len();
        func.locals.push(name);
        scope.push(id);

        id
    }

    /// Resolves a block and declares a list of locals inside of it
    fn resolve_block(&mut self, b: &mut Block, locals: Vec<String>) {
        let level = self.funcs.len() - 1;
        self.funcs[level].scopes.push(vec![]);
        for name in locals {
            self.add_local(name);
        }

        walk_block(b, self);

        let data = &mut self.funcs[level];
        let scope = data.scopes.pop().unwrap();
        for id in scope {
            let name = &data.locals[id];
            b.localmap.insert(name.clone(), id);
        }
    }

    /// Resolves the given named variable
    fn resolve_var(&mut self, name: &String, span: Span) -> _Variable {
        // first, try a local with that name
        if let Some(id) = self.funcs[self.funcs.len() - 1].get_local(&name) {
            VLocal(id)
        } else {
            // find an upvalue
            if let Some(id) = self.get_upval(&name) {
                VUpval(id)
            } else {
                // fall back to global access; resolve environment
                let envvar = Box::new(Spanned::new(span,
                    self.resolve_var(&"_ENV".to_string(), span)));

                VResGlobal(envvar, name.clone())
            }
        }
    }
}

impl Visitor for Resolver {
    fn visit_stmt(&mut self, s: &mut Stmt) {
        match s.value {
            SDecl(ref mut names, ref mut exprs) => {
                for expr in exprs {
                    walk_expr(expr, self);
                }

                for name in names {
                    self.add_local(name.clone());
                }
            },
            SLFunc(ref mut name, ref mut f) => {
                self.add_local(name.clone());
                self.visit_func(f);
            },
            SFor{ref var, ref mut body, ..} => {
                self.resolve_block(body, vec![var.clone()]);
            },
            SForIn{ref vars, ref mut body, ..} => {
                self.resolve_block(body, vars.clone());
            },
            _ => {
                walk_stmt(s, self);
            }
        }
    }

    fn visit_var(&mut self, v: &mut Variable) {
        {
            let ref mut var = v.value;

            if let VNamed(..) = *var {
                let newvar = if let VNamed(ref name) = *var {
                    self.resolve_var(name, v.span)
                } else { unreachable!(); };

                mem::replace(var, newvar);
                return;
            }
        }

        walk_var(v, self);
    }

    fn visit_block(&mut self, b: &mut Block) {
        self.resolve_block(b, vec![]);
    }

    fn visit_func(&mut self, f: &mut Function) {
        let mut data = FuncData::new(f.params.clone());
        if self.funcs.len() == 0 {
            // root function, add implicit _ENV upvalue
            data.add_upval("_ENV".to_string(), UpvalDesc::Upval(0));    // the UpvalDesc is ignored
        }

        self.funcs.push(data);
        self.visit_block(&mut f.value.body);

        let data = self.funcs.pop().unwrap();
        f.locals = data.locals;
        f.upvalues = data.upvals;
    }
}

/// Resolves all locals used in the given block. Recursively resolves all blocks found inside.
///
/// Allows blocks inside the given block to access locals declared within the parent block
/// (assuming they are declared before the block). Does not allow the given block to access outer
/// locals.
///
/// This also resolves any `VGlobal` to `VResGlobal` and resolves the function environments.
pub fn resolve_func(f: &mut Function) {
    Resolver {
        funcs: vec![],
    }.visit_func(f);
}

#[cfg(test)]
mod tests {
    use super::*;
    use parser::parse_main;
    use span::Spanned;
    use program::UpvalDesc;
    use ast::*;

    use std::default::Default;

    macro_rules! localmap {
        () => {{
            ::std::collections::HashMap::<String, usize>::new()
        }};
        ( $($key:ident: $e:expr),* ) => {{
            let mut m = ::std::collections::HashMap::<String, usize>::new();
            $( m.insert(stringify!($key).to_string(), $e); )*

            m
        }};
    }

    #[test]
    fn simple() {
        let mut f = parse_main(r#"
i = 0
local a
do
    local i
    local j = i
    i[j] = a
end
j = i
"#).unwrap();
        resolve_func(&mut f);

        assert_eq!(f.value.body, Block::with_locals(vec![
            Spanned::default(SAssign(
                vec![Spanned::default(VResGlobal(Box::new(Spanned::default(VUpval(0))), "i".to_string()))],
                vec![Spanned::default(ELit(TInt(0)))],
            )),
            Spanned::default(SDecl(vec!["a".to_string()], vec![])),
            Spanned::default(SDo(Block::with_locals(vec![
                Spanned::default(SDecl(vec!["i".to_string()], vec![])),
                Spanned::default(SDecl(vec!["j".to_string()], vec![
                    Spanned::default(EVar(Spanned::default(VLocal(1)))),
                ])),
                Spanned::default(SAssign(
                    vec![Spanned::default(VIndex(
                        Box::new(Spanned::default(VLocal(1))),
                        Box::new(Spanned::default(EVar(Spanned::default(VLocal(2)))))
                    ))],
                    vec![Spanned::default(EVar(Spanned::default(VLocal(0))))],
                )),
            ], Default::default(), localmap!{ i: 1, j: 2 }))),
            Spanned::default(SAssign(
                vec![Spanned::default(
                    VResGlobal(Box::new(Spanned::default(VUpval(0))), "j".to_string())
                )],
                vec![Spanned::default(EVar(Spanned::default(
                    VResGlobal(Box::new(Spanned::default(VUpval(0))), "i".to_string())
                )))],
            )),
        ], Default::default(), localmap!{ a: 0 }));
    }

    #[test]
    fn complex() {
        let mut f = parse_main(r#"
local a
local function f()
    f = nil     // upvalue
    local f = f // both

    local function g() f = a end    // chained upvalue + normal upvalue
end
"#).unwrap();
        resolve_func(&mut f);

        assert_eq!(f.value, _Function {
            params: vec![],
            varargs: true,
            locals: vec!["a".to_string(), "f".to_string()],
            upvalues: vec![UpvalDesc::Upval(0)],    // `_ENV`; the UpvalDesc is ignored
            body: Block::with_locals(vec![
                Spanned::default(SDecl(vec!["a".to_string()], vec![])),
                Spanned::default(SLFunc("f".to_string(), Spanned::default(_Function {
                    params: vec![],
                    varargs: false,
                    locals: vec!["f".to_string(), "g".to_string()],
                    upvalues: vec![UpvalDesc::Local(1), UpvalDesc::Local(0)],
                    body: Block::with_locals(vec![
                        Spanned::default(SAssign(vec![
                            Spanned::default(VUpval(0))
                        ], vec![
                            Spanned::default(ELit(TNil))
                        ])),
                        Spanned::default(SDecl(vec!["f".to_string()], vec![
                            Spanned::default(EVar(Spanned::default(VUpval(0))))
                        ])),
                        Spanned::default(SLFunc("g".to_string(), Spanned::default(_Function {
                            params: vec![],
                            varargs: false,
                            locals: vec![],
                            upvalues: vec![UpvalDesc::Local(0), UpvalDesc::Upval(1)],
                            body: Block::with_locals(vec![
                                Spanned::default(SAssign(vec![
                                    Spanned::default(VUpval(0))
                                ], vec![
                                    Spanned::default(EVar(Spanned::default(VUpval(1))))
                                ])),
                            ], Default::default(), localmap!{}),
                        }))),
                    ], Default::default(), localmap!{ f: 0, g: 1 }),
                }))),
            ], Default::default(), localmap!{ a: 0, f: 1 }),
        });
    }

    #[test]
    fn env_simple() {
        let mut f = parse_main("_ENV = 0").unwrap();
        resolve_func(&mut f);

        assert_eq!(f.value.body, Block::new(vec![
            Spanned::default(SAssign(vec![
                Spanned::default(VUpval(0))
            ], vec![
                Spanned::default(ELit(TInt(0)))
            ])),
        ], Default::default()));
    }
}
<|endoftext|>


<commit_before>#[macro_use]
pub mod proto;

use std::collections::HashMap;
use std::sync::Arc;
use std::io;
use std::time::Duration;
use std::sync::Mutex;
use std::thread;
use bincode::{SizeLimit, serde as bincode};
use tcp;
use utils::u8vec::*;

lazy_static! {
    pub static ref DEFAULT_CLIENT_POOL: ClientPool = ClientPool::new();
}

#[derive(Serialize, Deserialize, Debug)]
pub enum RPCRequestError {
    FunctionIdNotFound,
    ServiceIdNotFound,
    Other,
}

#[derive(Debug)]
pub enum RPCError {
    IOError(io::Error),
    RequestError(RPCRequestError),
}

pub trait RPCService: Sync + Send {
    fn dispatch(&self, data: Vec<u8>) -> Result<Vec<u8>, RPCRequestError>;
}

pub struct Server {
    services: HashMap<u64, Arc<RPCService>>,
}

pub struct ClientPool {
    clients: Mutex<HashMap<String, Arc<RPCSyncClient>>>
}

fn encode_res(res: Result<Vec<u8>, RPCRequestError>) -> Vec<u8> {
    match res {
        Ok(vec) => {
            [0u8; 1].iter().cloned().chain(vec.into_iter()).collect()
        },
        Err(e) => {
            let err_id = match e {
                RPCRequestError::FunctionIdNotFound => 1u8,
                RPCRequestError::ServiceIdNotFound => 2u8,
                _ => 255u8
            };
            vec!(err_id)
        }
    }
}

fn decode_res(res: io::Result<Vec<u8>>) -> Result<Vec<u8>, RPCError> {
    match res {
        Ok(res) => {
            if res[0] == 0u8 {
                Ok(res.into_iter().skip(1).collect())
            } else {
                match res[0] {
                    1u8 => Err(RPCError::RequestError(RPCRequestError::FunctionIdNotFound)),
                    2u8 => Err(RPCError::RequestError(RPCRequestError::ServiceIdNotFound)),
                    _ => Err(RPCError::RequestError(RPCRequestError::Other)),
                }
            }
        },
        Err(e) => Err(RPCError::IOError(e))
    }
}

impl Server {
    pub fn new(services: Vec<(u64, Arc<RPCService>)>) -> Arc<Server> {
        let mut svr_map = HashMap::with_capacity(services.len());
        for (svr_id, svr) in services {
            svr_map.insert(svr_id, svr.clone());
        }
        Arc::new(Server {
            services: svr_map,
        })
    }
    pub fn listen(server: Arc<Server>, addr: &String) {
        let server = server.clone();
        tcp::server::Server::new(addr, Box::new(move |data| {
            let (svr_id, data) = extract_u64_head(data);
            let service = server.services.get(&svr_id);
            match service {
                Some(service) => {
                    encode_res(service.dispatch(data))
                },
                None => encode_res(Err(RPCRequestError::ServiceIdNotFound) as Result<Vec<u8>, RPCRequestError>)
            }
        }));
    }
    pub fn listen_and_resume(server: Arc<Server>, addr: &String) {
        let server = server.clone();
        let addr = addr.clone();
        thread::spawn(move|| {
            Server::listen(server, &addr);
        });
    }
    pub fn append_service(&mut self, service_id: u64,  service: Arc<RPCService>) {
        self.services.insert(service_id, service);
    }
    pub fn remove_service(&mut self, service_id: u64) {
        self.services.remove(&service_id);
    }
}

pub struct RPCSyncClient {
    client: Mutex<tcp::client::Client>,
    pub address: String
}

impl RPCSyncClient {
    pub fn send(&self, svr_id: u64, data: Vec<u8>) -> Result<Vec<u8>, RPCError> {
        decode_res(self.client.lock().unwrap().send(prepend_u64(svr_id, data)))
    }

    pub fn new(addr: &String) -> io::Result<Arc<RPCSyncClient>> {
        Ok(Arc::new(RPCSyncClient {
            client: Mutex::new(tcp::client::Client::connect(addr)?),
            address: addr.clone()
        }))
    }
    pub fn with_timeout(addr: &String, timeout: Duration) -> io::Result<Arc<RPCSyncClient>> {
        Ok(Arc::new(RPCSyncClient {
            client: Mutex::new(tcp::client::Client::connect_with_timeout(addr, timeout)?),
            address: addr.clone()
        }))
    }
}

impl ClientPool {
    pub fn new() -> ClientPool {
        ClientPool {
            clients: Mutex::new(HashMap::new())
        }
    }

    pub fn get(&self, addr: &String) -> io::Result<Arc<RPCSyncClient>> {
        let mut clients = self.clients.lock().unwrap();
        if clients.contains_key(addr) {
            Ok(clients.get(addr).unwrap().clone())
        } else {
            let client = RPCSyncClient::new(addr);
            if let Ok(client) = client {
                clients.insert(addr.clone(), client.clone());
                Ok(client)
            } else {
                Err(client.err().unwrap())
            }
        }
    }
}<commit_msg>enable append and remove services without struct mutation<commit_after>#[macro_use]
pub mod proto;

use std::collections::HashMap;
use std::sync::Arc;
use std::io;
use std::time::Duration;
use std::sync::{Mutex, RwLock};
use std::thread;
use bincode::{SizeLimit, serde as bincode};
use tcp;
use utils::u8vec::*;

lazy_static! {
    pub static ref DEFAULT_CLIENT_POOL: ClientPool = ClientPool::new();
}

#[derive(Serialize, Deserialize, Debug)]
pub enum RPCRequestError {
    FunctionIdNotFound,
    ServiceIdNotFound,
    Other,
}

#[derive(Debug)]
pub enum RPCError {
    IOError(io::Error),
    RequestError(RPCRequestError),
}

pub trait RPCService: Sync + Send {
    fn dispatch(&self, data: Vec<u8>) -> Result<Vec<u8>, RPCRequestError>;
}

pub struct Server {
    services: RwLock<HashMap<u64, Arc<RPCService>>>,
}

pub struct ClientPool {
    clients: Mutex<HashMap<String, Arc<RPCSyncClient>>>
}

fn encode_res(res: Result<Vec<u8>, RPCRequestError>) -> Vec<u8> {
    match res {
        Ok(vec) => {
            [0u8; 1].iter().cloned().chain(vec.into_iter()).collect()
        },
        Err(e) => {
            let err_id = match e {
                RPCRequestError::FunctionIdNotFound => 1u8,
                RPCRequestError::ServiceIdNotFound => 2u8,
                _ => 255u8
            };
            vec!(err_id)
        }
    }
}

fn decode_res(res: io::Result<Vec<u8>>) -> Result<Vec<u8>, RPCError> {
    match res {
        Ok(res) => {
            if res[0] == 0u8 {
                Ok(res.into_iter().skip(1).collect())
            } else {
                match res[0] {
                    1u8 => Err(RPCError::RequestError(RPCRequestError::FunctionIdNotFound)),
                    2u8 => Err(RPCError::RequestError(RPCRequestError::ServiceIdNotFound)),
                    _ => Err(RPCError::RequestError(RPCRequestError::Other)),
                }
            }
        },
        Err(e) => Err(RPCError::IOError(e))
    }
}

impl Server {
    pub fn new(services: Vec<(u64, Arc<RPCService>)>) -> Arc<Server> {
        let mut svr_map = HashMap::with_capacity(services.len());
        for (svr_id, svr) in services {
            svr_map.insert(svr_id, svr.clone());
        }
        Arc::new(Server {
            services: RwLock::new(svr_map),
        })
    }
    pub fn listen(server: Arc<Server>, addr: &String) {
        let server = server.clone();
        tcp::server::Server::new(addr, Box::new(move |data| {
            let (svr_id, data) = extract_u64_head(data);
            let svr_map = server.services.read().unwrap();
            let service = svr_map.get(&svr_id);
            match service {
                Some(service) => {
                    encode_res(service.dispatch(data))
                },
                None => encode_res(Err(RPCRequestError::ServiceIdNotFound) as Result<Vec<u8>, RPCRequestError>)
            }
        }));
    }
    pub fn listen_and_resume(server: Arc<Server>, addr: &String) {
        let server = server.clone();
        let addr = addr.clone();
        thread::spawn(move|| {
            Server::listen(server, &addr);
        });
    }
    pub fn append_service(&self, service_id: u64,  service: Arc<RPCService>) {
        self.services.write().unwrap().insert(service_id, service);
    }
    pub fn remove_service(&self, service_id: u64) {
        self.services.write().unwrap().remove(&service_id);
    }
}

pub struct RPCSyncClient {
    client: Mutex<tcp::client::Client>,
    pub address: String
}

impl RPCSyncClient {
    pub fn send(&self, svr_id: u64, data: Vec<u8>) -> Result<Vec<u8>, RPCError> {
        decode_res(self.client.lock().unwrap().send(prepend_u64(svr_id, data)))
    }

    pub fn new(addr: &String) -> io::Result<Arc<RPCSyncClient>> {
        Ok(Arc::new(RPCSyncClient {
            client: Mutex::new(tcp::client::Client::connect(addr)?),
            address: addr.clone()
        }))
    }
    pub fn with_timeout(addr: &String, timeout: Duration) -> io::Result<Arc<RPCSyncClient>> {
        Ok(Arc::new(RPCSyncClient {
            client: Mutex::new(tcp::client::Client::connect_with_timeout(addr, timeout)?),
            address: addr.clone()
        }))
    }
}

impl ClientPool {
    pub fn new() -> ClientPool {
        ClientPool {
            clients: Mutex::new(HashMap::new())
        }
    }

    pub fn get(&self, addr: &String) -> io::Result<Arc<RPCSyncClient>> {
        let mut clients = self.clients.lock().unwrap();
        if clients.contains_key(addr) {
            Ok(clients.get(addr).unwrap().clone())
        } else {
            let client = RPCSyncClient::new(addr);
            if let Ok(client) = client {
                clients.insert(addr.clone(), client.clone());
                Ok(client)
            } else {
                Err(client.err().unwrap())
            }
        }
    }
}<|endoftext|>