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

//! Abstractions for global scopes.
//!
//! This module contains smart pointers to global scopes, to simplify writing
//! code that works in workers as well as window scopes.

use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::conversions::root_from_object;
use dom::bindings::error::ErrorInfo;
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflectable, Reflector};
use dom::console::TimerSet;
use dom::window::{self, ScriptHelpers};
use dom::workerglobalscope::WorkerGlobalScope;
use ipc_channel::ipc::IpcSender;
use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
use js::glue::{IsWrapper, UnwrapObject};
use js::jsapi::{CurrentGlobalOrNull, GetGlobalForObjectCrossCompartment};
use js::jsapi::{JSContext, JSObject, JS_GetClass, MutableHandleValue};
use js::jsapi::HandleValue;
use msg::constellation_msg::PipelineId;
use net_traits::{CoreResourceThread, IpcSend, ResourceThreads};
use profile_traits::{mem, time};
use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, EnqueuedPromiseCallback};
use script_thread::{MainThreadScriptChan, RunnableWrapper, ScriptThread};
use script_traits::{MsDuration, ScriptMsg as ConstellationMsg, TimerEventRequest};
use task_source::file_reading::FileReadingTaskSource;
use timers::{OneshotTimerCallback, OneshotTimerHandle};
use url::Url;

/// A freely-copyable reference to a rooted global object.
#[derive(Copy, Clone)]
pub enum GlobalRef<'a> {
    /// A reference to a `Window` object.
    Window(&'a window::Window),
    /// A reference to a `WorkerGlobalScope` object.
    Worker(&'a WorkerGlobalScope),
}

/// A stack-based rooted reference to a global object.
pub enum GlobalRoot {
    /// A root for a `Window` object.
    Window(Root<window::Window>),
    /// A root for a `WorkerGlobalScope` object.
    Worker(Root<WorkerGlobalScope>),
}

impl<'a> GlobalRef<'a> {
    /// Get the `JSContext` for the `JSRuntime` associated with the thread
    /// this global object is on.
    pub fn get_cx(&self) -> *mut JSContext {
        match *self {
            GlobalRef::Window(ref window) => window.get_cx(),
            GlobalRef::Worker(ref worker) => worker.get_cx(),
        }
    }

    /// Extract a `Window`, causing thread failure if the global object is not
    /// a `Window`.
    pub fn as_window(&self) -> &window::Window {
        match *self {
            GlobalRef::Window(window) => window,
            GlobalRef::Worker(_) => panic!("expected a Window scope"),
        }
    }

    /// Get the `PipelineId` for this global scope.
    pub fn pipeline_id(&self) -> PipelineId {
        match *self {
            GlobalRef::Window(window) => window.pipeline_id(),
            GlobalRef::Worker(worker) => worker.pipeline_id(),
        }
    }

    /// Get a `mem::ProfilerChan` to send messages to the memory profiler thread.
    pub fn mem_profiler_chan(&self) -> &mem::ProfilerChan {
        match *self {
            GlobalRef::Window(window) => window.mem_profiler_chan(),
            GlobalRef::Worker(worker) => worker.mem_profiler_chan(),
        }
    }

    /// Get a `time::ProfilerChan` to send messages to the time profiler thread.
    pub fn time_profiler_chan(&self) -> &time::ProfilerChan {
        match *self {
            GlobalRef::Window(window) => window.time_profiler_chan(),
            GlobalRef::Worker(worker) => worker.time_profiler_chan(),
        }
    }

    /// Get a `IpcSender` to send messages to the constellation when available.
    pub fn constellation_chan(&self) -> &IpcSender<ConstellationMsg> {
        match *self {
            GlobalRef::Window(window) => window.constellation_chan(),
            GlobalRef::Worker(worker) => worker.constellation_chan(),
        }
    }

    /// Get the scheduler channel to request timer events.
    pub fn scheduler_chan(&self) -> &IpcSender<TimerEventRequest> {
        match *self {
            GlobalRef::Window(window) => window.scheduler_chan(),
            GlobalRef::Worker(worker) => worker.scheduler_chan(),
        }
    }

    /// Get an `IpcSender<ScriptToDevtoolsControlMsg>` to send messages to Devtools
    /// thread when available.
    pub fn devtools_chan(&self) -> Option<IpcSender<ScriptToDevtoolsControlMsg>> {
        match *self {
            GlobalRef::Window(window) => window.devtools_chan(),
            GlobalRef::Worker(worker) => worker.devtools_chan(),
        }
    }

    /// Get the `ResourceThreads` for this global scope.
    pub fn resource_threads(&self) -> ResourceThreads {
        match *self {
            GlobalRef::Window(ref window) => {
                let doc = window.Document();
                let doc = doc.r();
                let loader = doc.loader();
                loader.resource_threads().clone()
            }
            GlobalRef::Worker(ref worker) => worker.resource_threads().clone(),
        }
    }

    /// Get the `CoreResourceThread` for this global scope
    pub fn core_resource_thread(&self) -> CoreResourceThread {
        self.resource_threads().sender()
    }

    /// Get the worker's id.
    pub fn get_worker_id(&self) -> Option<WorkerId> {
        match *self {
            GlobalRef::Window(_) => None,
            GlobalRef::Worker(ref worker) => Some(worker.get_worker_id()),
        }
    }

    /// Get next worker id.
    pub fn get_next_worker_id(&self) -> WorkerId {
        match *self {
            GlobalRef::Window(ref window) => window.get_next_worker_id(),
            GlobalRef::Worker(ref worker) => worker.get_next_worker_id(),
        }
    }

    /// Get the URL for this global scope.
    pub fn get_url(&self) -> Url {
        match *self {
            GlobalRef::Window(ref window) => window.get_url(),
            GlobalRef::Worker(ref worker) => worker.get_url().clone(),
        }
    }

    /// Get the [base url](https://html.spec.whatwg.org/multipage/#api-base-url)
    /// for this global scope.
    pub fn api_base_url(&self) -> Url {
        match *self {
            // https://html.spec.whatwg.org/multipage/#script-settings-for-browsing-contexts:api-base-url
            GlobalRef::Window(ref window) => window.Document().base_url(),
            // https://html.spec.whatwg.org/multipage/#script-settings-for-workers:api-base-url
            GlobalRef::Worker(ref worker) => worker.get_url().clone(),
        }
    }

    /// `ScriptChan` used to send messages to the event loop of this global's
    /// thread.
    pub fn script_chan(&self) -> Box<ScriptChan + Send> {
        match *self {
            GlobalRef::Window(ref window) =>
                MainThreadScriptChan(window.main_thread_script_chan().clone()).clone(),
            GlobalRef::Worker(ref worker) => worker.script_chan(),
        }
    }

    /// `ScriptChan` used to send messages to the event loop of this global's
    /// thread.
    pub fn networking_task_source(&self) -> Box<ScriptChan + Send> {
        match *self {
            GlobalRef::Window(ref window) => window.networking_task_source(),
            GlobalRef::Worker(ref worker) => worker.script_chan(),
        }
    }

    /// `ScriptChan` used to send messages to the event loop of this global's
    /// thread.
    pub fn history_traversal_task_source(&self) -> Box<ScriptChan + Send> {
        match *self {
            GlobalRef::Window(ref window) => window.history_traversal_task_source(),
            GlobalRef::Worker(ref worker) => worker.script_chan(),
        }
    }

    /// `ScriptChan` used to send messages to the event loop of this global's
    /// thread.
    pub fn file_reading_task_source(&self) -> FileReadingTaskSource {
        match *self {
            GlobalRef::Window(ref window) => window.file_reading_task_source(),
            GlobalRef::Worker(ref worker) => worker.file_reading_task_source(),
        }
    }

    /// Create a new sender/receiver pair that can be used to implement an on-demand
    /// event loop. Used for implementing web APIs that require blocking semantics
    /// without resorting to nested event loops.
    pub fn new_script_pair(&self) -> (Box<ScriptChan + Send>, Box<ScriptPort + Send>) {
        match *self {
            GlobalRef::Window(ref window) => window.new_script_pair(),
            GlobalRef::Worker(ref worker) => worker.new_script_pair(),
        }
    }

    /// Process a single event as if it were the next event in the thread queue for
    /// this global.
    pub fn process_event(&self, msg: CommonScriptMsg) {
        match *self {
            GlobalRef::Window(_) => ScriptThread::process_event(msg),
            GlobalRef::Worker(ref worker) => worker.process_event(msg),
        }
    }

    /// Evaluate the JS messages on the `RootedValue` of this global
    pub fn evaluate_js_on_global_with_result(&self, code: &str, rval: MutableHandleValue) {
        match *self {
            GlobalRef::Window(window) => window.evaluate_js_on_global_with_result(code, rval),
            GlobalRef::Worker(worker) => worker.evaluate_js_on_global_with_result(code, rval),
        }
    }

    /// Set the `bool` value to indicate whether developer tools has requested
    /// updates from the global
    pub fn set_devtools_wants_updates(&self, send_updates: bool) {
        match *self {
            GlobalRef::Window(window) => window.set_devtools_wants_updates(send_updates),
            GlobalRef::Worker(worker) => worker.set_devtools_wants_updates(send_updates),
        }
    }

    /// Schedule the given `callback` to be invoked after at least `duration` milliseconds have
    /// passed.
    pub fn schedule_callback(&self,
                             callback: OneshotTimerCallback,
                             duration: MsDuration)
                             -> OneshotTimerHandle {
        match *self {
            GlobalRef::Window(window) => window.schedule_callback(callback, duration),
            GlobalRef::Worker(worker) => worker.schedule_callback(callback, duration),
        }
    }

    /// Unschedule a previously-scheduled callback.
    pub fn unschedule_callback(&self, handle: OneshotTimerHandle) {
        match *self {
            GlobalRef::Window(window) => window.unschedule_callback(handle),
            GlobalRef::Worker(worker) => worker.unschedule_callback(handle),
        }
    }

    /// Returns the global's timers for the Console API.
    pub fn console_timers(&self) -> &TimerSet {
        match *self {
            GlobalRef::Window(ref window) => window.console_timers(),
            GlobalRef::Worker(ref worker) => worker.console_timers(),
        }
    }

    /// Returns a wrapper for runnables to ensure they are cancelled if the global
    /// is being destroyed.
    pub fn get_runnable_wrapper(&self) -> RunnableWrapper {
        match *self {
            GlobalRef::Window(ref window) => window.get_runnable_wrapper(),
            GlobalRef::Worker(ref worker) => worker.get_runnable_wrapper(),
        }
    }

    /// Enqueue a promise callback for subsequent execution.
    pub fn enqueue_promise_job(&self, job: EnqueuedPromiseCallback) {
        match *self {
            GlobalRef::Window(_) => ScriptThread::enqueue_promise_job(job, *self),
            GlobalRef::Worker(ref worker) => worker.enqueue_promise_job(job),
        }
    }

    /// Start the process of executing the pending promise callbacks. They will be invoked
    /// in FIFO order, synchronously, at some point in the future.
    pub fn flush_promise_jobs(&self) {
        match *self {
            GlobalRef::Window(_) => ScriptThread::flush_promise_jobs(*self),
            GlobalRef::Worker(ref worker) => worker.flush_promise_jobs(),
        }
    }

    /// https://html.spec.whatwg.org/multipage/#report-the-error
    pub fn report_an_error(&self, error_info: ErrorInfo, value: HandleValue) {
        match *self {
            GlobalRef::Window(ref window) => window.report_an_error(error_info, value),
            GlobalRef::Worker(ref worker) => worker.report_an_error(error_info, value),
        }
    }
}

impl<'a> Reflectable for GlobalRef<'a> {
    fn reflector(&self) -> &Reflector {
        match *self {
            GlobalRef::Window(ref window) => window.reflector(),
            GlobalRef::Worker(ref worker) => worker.reflector(),
        }
    }
}

impl GlobalRoot {
    /// Obtain a safe reference to the global object that cannot outlive the
    /// lifetime of this root.
    pub fn r(&self) -> GlobalRef {
        match *self {
            GlobalRoot::Window(ref window) => GlobalRef::Window(window.r()),
            GlobalRoot::Worker(ref worker) => GlobalRef::Worker(worker.r()),
        }
    }
}

/// Returns the global object of the realm that the given DOM object's reflector was created in.
pub fn global_root_from_reflector<T: Reflectable>(reflector: &T) -> GlobalRoot {
    unsafe { global_root_from_object(*reflector.reflector().get_jsobject()) }
}

/// Returns the Rust global object from a JS global object.
#[allow(unrooted_must_root)]
unsafe fn global_root_from_global(global: *mut JSObject) -> GlobalRoot {
    assert!(!global.is_null());
    let clasp = JS_GetClass(global);
    assert!(((*clasp).flags & (JSCLASS_IS_DOMJSCLASS | JSCLASS_IS_GLOBAL)) != 0);
    match root_from_object(global) {
        Ok(window) => return GlobalRoot::Window(window),
        Err(_) => (),
    }

    match root_from_object(global) {
        Ok(worker) => return GlobalRoot::Worker(worker),
        Err(_) => (),
    }

    panic!("found DOM global that doesn't unwrap to Window or WorkerGlobalScope")
}

/// Returns the global object of the realm that the given JS object was created in.
#[allow(unrooted_must_root)]
pub unsafe fn global_root_from_object(obj: *mut JSObject) -> GlobalRoot {
    assert!(!obj.is_null());
    let global = GetGlobalForObjectCrossCompartment(obj);
    global_root_from_global(global)
}

/// Returns the global object for the given JSContext
#[allow(unrooted_must_root)]
pub unsafe fn global_root_from_context(cx: *mut JSContext) -> GlobalRoot {
    let global = CurrentGlobalOrNull(cx);
    global_root_from_global(global)
}

/// Returns the global object of the realm that the given JS object was created in,
/// after unwrapping any wrappers.
pub unsafe fn global_root_from_object_maybe_wrapped(mut obj: *mut JSObject) -> GlobalRoot {
    if IsWrapper(obj) {
        obj = UnwrapObject(obj, /* stopAtWindowProxy = */ 0);
        assert!(!obj.is_null());
    }
    global_root_from_object(obj)
}
<commit_msg>Remove GlobalRef::history_traversal_task_source<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Abstractions for global scopes.
//!
//! This module contains smart pointers to global scopes, to simplify writing
//! code that works in workers as well as window scopes.

use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::conversions::root_from_object;
use dom::bindings::error::ErrorInfo;
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflectable, Reflector};
use dom::console::TimerSet;
use dom::window::{self, ScriptHelpers};
use dom::workerglobalscope::WorkerGlobalScope;
use ipc_channel::ipc::IpcSender;
use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
use js::glue::{IsWrapper, UnwrapObject};
use js::jsapi::{CurrentGlobalOrNull, GetGlobalForObjectCrossCompartment};
use js::jsapi::{JSContext, JSObject, JS_GetClass, MutableHandleValue};
use js::jsapi::HandleValue;
use msg::constellation_msg::PipelineId;
use net_traits::{CoreResourceThread, IpcSend, ResourceThreads};
use profile_traits::{mem, time};
use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, EnqueuedPromiseCallback};
use script_thread::{MainThreadScriptChan, RunnableWrapper, ScriptThread};
use script_traits::{MsDuration, ScriptMsg as ConstellationMsg, TimerEventRequest};
use task_source::file_reading::FileReadingTaskSource;
use timers::{OneshotTimerCallback, OneshotTimerHandle};
use url::Url;

/// A freely-copyable reference to a rooted global object.
#[derive(Copy, Clone)]
pub enum GlobalRef<'a> {
    /// A reference to a `Window` object.
    Window(&'a window::Window),
    /// A reference to a `WorkerGlobalScope` object.
    Worker(&'a WorkerGlobalScope),
}

/// A stack-based rooted reference to a global object.
pub enum GlobalRoot {
    /// A root for a `Window` object.
    Window(Root<window::Window>),
    /// A root for a `WorkerGlobalScope` object.
    Worker(Root<WorkerGlobalScope>),
}

impl<'a> GlobalRef<'a> {
    /// Get the `JSContext` for the `JSRuntime` associated with the thread
    /// this global object is on.
    pub fn get_cx(&self) -> *mut JSContext {
        match *self {
            GlobalRef::Window(ref window) => window.get_cx(),
            GlobalRef::Worker(ref worker) => worker.get_cx(),
        }
    }

    /// Extract a `Window`, causing thread failure if the global object is not
    /// a `Window`.
    pub fn as_window(&self) -> &window::Window {
        match *self {
            GlobalRef::Window(window) => window,
            GlobalRef::Worker(_) => panic!("expected a Window scope"),
        }
    }

    /// Get the `PipelineId` for this global scope.
    pub fn pipeline_id(&self) -> PipelineId {
        match *self {
            GlobalRef::Window(window) => window.pipeline_id(),
            GlobalRef::Worker(worker) => worker.pipeline_id(),
        }
    }

    /// Get a `mem::ProfilerChan` to send messages to the memory profiler thread.
    pub fn mem_profiler_chan(&self) -> &mem::ProfilerChan {
        match *self {
            GlobalRef::Window(window) => window.mem_profiler_chan(),
            GlobalRef::Worker(worker) => worker.mem_profiler_chan(),
        }
    }

    /// Get a `time::ProfilerChan` to send messages to the time profiler thread.
    pub fn time_profiler_chan(&self) -> &time::ProfilerChan {
        match *self {
            GlobalRef::Window(window) => window.time_profiler_chan(),
            GlobalRef::Worker(worker) => worker.time_profiler_chan(),
        }
    }

    /// Get a `IpcSender` to send messages to the constellation when available.
    pub fn constellation_chan(&self) -> &IpcSender<ConstellationMsg> {
        match *self {
            GlobalRef::Window(window) => window.constellation_chan(),
            GlobalRef::Worker(worker) => worker.constellation_chan(),
        }
    }

    /// Get the scheduler channel to request timer events.
    pub fn scheduler_chan(&self) -> &IpcSender<TimerEventRequest> {
        match *self {
            GlobalRef::Window(window) => window.scheduler_chan(),
            GlobalRef::Worker(worker) => worker.scheduler_chan(),
        }
    }

    /// Get an `IpcSender<ScriptToDevtoolsControlMsg>` to send messages to Devtools
    /// thread when available.
    pub fn devtools_chan(&self) -> Option<IpcSender<ScriptToDevtoolsControlMsg>> {
        match *self {
            GlobalRef::Window(window) => window.devtools_chan(),
            GlobalRef::Worker(worker) => worker.devtools_chan(),
        }
    }

    /// Get the `ResourceThreads` for this global scope.
    pub fn resource_threads(&self) -> ResourceThreads {
        match *self {
            GlobalRef::Window(ref window) => {
                let doc = window.Document();
                let doc = doc.r();
                let loader = doc.loader();
                loader.resource_threads().clone()
            }
            GlobalRef::Worker(ref worker) => worker.resource_threads().clone(),
        }
    }

    /// Get the `CoreResourceThread` for this global scope
    pub fn core_resource_thread(&self) -> CoreResourceThread {
        self.resource_threads().sender()
    }

    /// Get the worker's id.
    pub fn get_worker_id(&self) -> Option<WorkerId> {
        match *self {
            GlobalRef::Window(_) => None,
            GlobalRef::Worker(ref worker) => Some(worker.get_worker_id()),
        }
    }

    /// Get next worker id.
    pub fn get_next_worker_id(&self) -> WorkerId {
        match *self {
            GlobalRef::Window(ref window) => window.get_next_worker_id(),
            GlobalRef::Worker(ref worker) => worker.get_next_worker_id(),
        }
    }

    /// Get the URL for this global scope.
    pub fn get_url(&self) -> Url {
        match *self {
            GlobalRef::Window(ref window) => window.get_url(),
            GlobalRef::Worker(ref worker) => worker.get_url().clone(),
        }
    }

    /// Get the [base url](https://html.spec.whatwg.org/multipage/#api-base-url)
    /// for this global scope.
    pub fn api_base_url(&self) -> Url {
        match *self {
            // https://html.spec.whatwg.org/multipage/#script-settings-for-browsing-contexts:api-base-url
            GlobalRef::Window(ref window) => window.Document().base_url(),
            // https://html.spec.whatwg.org/multipage/#script-settings-for-workers:api-base-url
            GlobalRef::Worker(ref worker) => worker.get_url().clone(),
        }
    }

    /// `ScriptChan` used to send messages to the event loop of this global's
    /// thread.
    pub fn script_chan(&self) -> Box<ScriptChan + Send> {
        match *self {
            GlobalRef::Window(ref window) =>
                MainThreadScriptChan(window.main_thread_script_chan().clone()).clone(),
            GlobalRef::Worker(ref worker) => worker.script_chan(),
        }
    }

    /// `ScriptChan` used to send messages to the event loop of this global's
    /// thread.
    pub fn networking_task_source(&self) -> Box<ScriptChan + Send> {
        match *self {
            GlobalRef::Window(ref window) => window.networking_task_source(),
            GlobalRef::Worker(ref worker) => worker.script_chan(),
        }
    }

    /// `ScriptChan` used to send messages to the event loop of this global's
    /// thread.
    pub fn file_reading_task_source(&self) -> FileReadingTaskSource {
        match *self {
            GlobalRef::Window(ref window) => window.file_reading_task_source(),
            GlobalRef::Worker(ref worker) => worker.file_reading_task_source(),
        }
    }

    /// Create a new sender/receiver pair that can be used to implement an on-demand
    /// event loop. Used for implementing web APIs that require blocking semantics
    /// without resorting to nested event loops.
    pub fn new_script_pair(&self) -> (Box<ScriptChan + Send>, Box<ScriptPort + Send>) {
        match *self {
            GlobalRef::Window(ref window) => window.new_script_pair(),
            GlobalRef::Worker(ref worker) => worker.new_script_pair(),
        }
    }

    /// Process a single event as if it were the next event in the thread queue for
    /// this global.
    pub fn process_event(&self, msg: CommonScriptMsg) {
        match *self {
            GlobalRef::Window(_) => ScriptThread::process_event(msg),
            GlobalRef::Worker(ref worker) => worker.process_event(msg),
        }
    }

    /// Evaluate the JS messages on the `RootedValue` of this global
    pub fn evaluate_js_on_global_with_result(&self, code: &str, rval: MutableHandleValue) {
        match *self {
            GlobalRef::Window(window) => window.evaluate_js_on_global_with_result(code, rval),
            GlobalRef::Worker(worker) => worker.evaluate_js_on_global_with_result(code, rval),
        }
    }

    /// Set the `bool` value to indicate whether developer tools has requested
    /// updates from the global
    pub fn set_devtools_wants_updates(&self, send_updates: bool) {
        match *self {
            GlobalRef::Window(window) => window.set_devtools_wants_updates(send_updates),
            GlobalRef::Worker(worker) => worker.set_devtools_wants_updates(send_updates),
        }
    }

    /// Schedule the given `callback` to be invoked after at least `duration` milliseconds have
    /// passed.
    pub fn schedule_callback(&self,
                             callback: OneshotTimerCallback,
                             duration: MsDuration)
                             -> OneshotTimerHandle {
        match *self {
            GlobalRef::Window(window) => window.schedule_callback(callback, duration),
            GlobalRef::Worker(worker) => worker.schedule_callback(callback, duration),
        }
    }

    /// Unschedule a previously-scheduled callback.
    pub fn unschedule_callback(&self, handle: OneshotTimerHandle) {
        match *self {
            GlobalRef::Window(window) => window.unschedule_callback(handle),
            GlobalRef::Worker(worker) => worker.unschedule_callback(handle),
        }
    }

    /// Returns the global's timers for the Console API.
    pub fn console_timers(&self) -> &TimerSet {
        match *self {
            GlobalRef::Window(ref window) => window.console_timers(),
            GlobalRef::Worker(ref worker) => worker.console_timers(),
        }
    }

    /// Returns a wrapper for runnables to ensure they are cancelled if the global
    /// is being destroyed.
    pub fn get_runnable_wrapper(&self) -> RunnableWrapper {
        match *self {
            GlobalRef::Window(ref window) => window.get_runnable_wrapper(),
            GlobalRef::Worker(ref worker) => worker.get_runnable_wrapper(),
        }
    }

    /// Enqueue a promise callback for subsequent execution.
    pub fn enqueue_promise_job(&self, job: EnqueuedPromiseCallback) {
        match *self {
            GlobalRef::Window(_) => ScriptThread::enqueue_promise_job(job, *self),
            GlobalRef::Worker(ref worker) => worker.enqueue_promise_job(job),
        }
    }

    /// Start the process of executing the pending promise callbacks. They will be invoked
    /// in FIFO order, synchronously, at some point in the future.
    pub fn flush_promise_jobs(&self) {
        match *self {
            GlobalRef::Window(_) => ScriptThread::flush_promise_jobs(*self),
            GlobalRef::Worker(ref worker) => worker.flush_promise_jobs(),
        }
    }

    /// https://html.spec.whatwg.org/multipage/#report-the-error
    pub fn report_an_error(&self, error_info: ErrorInfo, value: HandleValue) {
        match *self {
            GlobalRef::Window(ref window) => window.report_an_error(error_info, value),
            GlobalRef::Worker(ref worker) => worker.report_an_error(error_info, value),
        }
    }
}

impl<'a> Reflectable for GlobalRef<'a> {
    fn reflector(&self) -> &Reflector {
        match *self {
            GlobalRef::Window(ref window) => window.reflector(),
            GlobalRef::Worker(ref worker) => worker.reflector(),
        }
    }
}

impl GlobalRoot {
    /// Obtain a safe reference to the global object that cannot outlive the
    /// lifetime of this root.
    pub fn r(&self) -> GlobalRef {
        match *self {
            GlobalRoot::Window(ref window) => GlobalRef::Window(window.r()),
            GlobalRoot::Worker(ref worker) => GlobalRef::Worker(worker.r()),
        }
    }
}

/// Returns the global object of the realm that the given DOM object's reflector was created in.
pub fn global_root_from_reflector<T: Reflectable>(reflector: &T) -> GlobalRoot {
    unsafe { global_root_from_object(*reflector.reflector().get_jsobject()) }
}

/// Returns the Rust global object from a JS global object.
#[allow(unrooted_must_root)]
unsafe fn global_root_from_global(global: *mut JSObject) -> GlobalRoot {
    assert!(!global.is_null());
    let clasp = JS_GetClass(global);
    assert!(((*clasp).flags & (JSCLASS_IS_DOMJSCLASS | JSCLASS_IS_GLOBAL)) != 0);
    match root_from_object(global) {
        Ok(window) => return GlobalRoot::Window(window),
        Err(_) => (),
    }

    match root_from_object(global) {
        Ok(worker) => return GlobalRoot::Worker(worker),
        Err(_) => (),
    }

    panic!("found DOM global that doesn't unwrap to Window or WorkerGlobalScope")
}

/// Returns the global object of the realm that the given JS object was created in.
#[allow(unrooted_must_root)]
pub unsafe fn global_root_from_object(obj: *mut JSObject) -> GlobalRoot {
    assert!(!obj.is_null());
    let global = GetGlobalForObjectCrossCompartment(obj);
    global_root_from_global(global)
}

/// Returns the global object for the given JSContext
#[allow(unrooted_must_root)]
pub unsafe fn global_root_from_context(cx: *mut JSContext) -> GlobalRoot {
    let global = CurrentGlobalOrNull(cx);
    global_root_from_global(global)
}

/// Returns the global object of the realm that the given JS object was created in,
/// after unwrapping any wrappers.
pub unsafe fn global_root_from_object_maybe_wrapped(mut obj: *mut JSObject) -> GlobalRoot {
    if IsWrapper(obj) {
        obj = UnwrapObject(obj, /* stopAtWindowProxy = */ 0);
        assert!(!obj.is_null());
    }
    global_root_from_object(obj)
}
<|endoftext|>


<commit_before>fn main() {
    let mut op = Some(2);
    match op {
        Some(ref v) => { let a = &mut v; },
        //~^ ERROR cannot borrow `v` as mutable, as it is not declared as mutable
        //~^ HELP try removing `&mut` here
        None => {},
    }
}
<commit_msg>fix test/ui/borrowck/issue-33819<commit_after>fn main() {
    let mut op = Some(2);
    match op {
        Some(ref v) => { let a = &mut v; },
        //~^ ERROR cannot borrow `v` as mutable, as it is not declared as mutable
        //~| HELP try removing `&mut` here
        None => {},
    }
}
<|endoftext|>


<commit_before>mod key_code;
pub use self::key_code::KeyCode;

use mio::unix::EventedFd;
use mio::{Events, Poll, PollOpt, Ready, Registration, SetReadiness, Token};
use promising_future::{future_promise, Promise};
use std::collections::hash_map::{Entry, HashMap};
use std::os::raw::{c_int, c_uint, c_ulong};
use std::sync::mpsc::{channel, Sender};
use std::thread::{self, JoinHandle};
use std::{mem, ptr};
use x11_dl::xlib::{
    Display, GrabModeAsync, KeyPress, KeyPressMask, Mod2Mask, XErrorEvent, XKeyEvent, Xlib,
};

#[derive(Debug, snafu::Snafu)]
pub enum Error {
    NoXLib,
    OpenXServerConnection,
    EPoll,
    ThreadStopped,
    AlreadyRegistered,
    NotRegistered,
}

pub type Result<T> = std::result::Result<T, Error>;

enum Message {
    Register(
        KeyCode,
        Box<dyn FnMut() + Send + 'static>,
        Promise<Result<()>>,
    ),
    Unregister(KeyCode, Promise<Result<()>>),
    End,
}

pub struct Hook {
    sender: Sender<Message>,
    ping: SetReadiness,
    _registration: Registration,
    join_handle: Option<JoinHandle<Result<()>>>,
}

impl Drop for Hook {
    fn drop(&mut self) {
        self.sender.send(Message::End).ok();
        self.ping.set_readiness(Ready::readable()).ok();
        if let Some(handle) = self.join_handle.take() {
            handle.join().ok();
        }
    }
}

unsafe fn unregister(xlib: &Xlib, display: *mut Display, window: c_ulong, code: c_uint) {
    (xlib.XUngrabKey)(display, code as _, 0, window);
    (xlib.XUngrabKey)(display, code as _, Mod2Mask, window);
}

unsafe extern "C" fn handle_error(_: *mut Display, _: *mut XErrorEvent) -> c_int {
    0
}

impl Hook {
    pub fn new() -> Result<Self> {
        unsafe {
            let (sender, receiver) = channel();

            let xlib = Xlib::open().map_err(|_| Error::NoXLib)?;
            (xlib.XSetErrorHandler)(Some(handle_error));

            let display = (xlib.XOpenDisplay)(ptr::null());
            if display.is_null() {
                return Err(Error::OpenXServerConnection);
            }

            let window = (xlib.XDefaultRootWindow)(display);
            (xlib.XSelectInput)(display, window, KeyPressMask);

            let fd = (xlib.XConnectionNumber)(display);
            let poll = Poll::new().map_err(|_| Error::EPoll)?;

            let (registration, ping) = Registration::new2();

            const X_TOKEN: Token = Token(0);
            const PING_TOKEN: Token = Token(1);

            poll.register(
                &EventedFd(&fd),
                X_TOKEN,
                Ready::readable() | Ready::writable(),
                PollOpt::edge(),
            )
            .map_err(|_| Error::EPoll)?;

            poll.register(
                &registration,
                PING_TOKEN,
                Ready::readable(),
                PollOpt::edge(),
            )
            .map_err(|_| Error::EPoll)?;

            struct XData(Xlib, *mut Display, c_ulong);
            unsafe impl Send for XData {}
            let xdata = XData(xlib, display, window);

            let join_handle = thread::spawn(move || -> Result<()> {
                let XData(xlib, display, window) = xdata;

                let mut result = Ok(());
                let mut events = Events::with_capacity(1024);
                let mut hotkeys = HashMap::new();

                'event_loop: loop {
                    if poll.poll(&mut events, None).is_err() {
                        result = Err(Error::EPoll);
                        break 'event_loop;
                    }

                    for mio_event in &events {
                        if mio_event.token() == PING_TOKEN {
                            for message in receiver.try_iter() {
                                match message {
                                    Message::Register(key, callback, promise) => {
                                        let code =
                                            (xlib.XKeysymToKeycode)(display, key as _) as c_uint;

                                        if let Entry::Vacant(vacant) = hotkeys.entry(code) {
                                            (xlib.XGrabKey)(
                                                display,
                                                code as _,
                                                0,
                                                window,
                                                false as _,
                                                GrabModeAsync,
                                                GrabModeAsync,
                                            );

                                            (xlib.XGrabKey)(
                                                display,
                                                code as _,
                                                Mod2Mask,
                                                window,
                                                false as _,
                                                GrabModeAsync,
                                                GrabModeAsync,
                                            );

                                            vacant.insert(callback);
                                            promise.set(Ok(()));
                                        } else {
                                            promise.set(Err(Error::AlreadyRegistered));
                                        }
                                    }
                                    Message::Unregister(key, promise) => {
                                        let code =
                                            (xlib.XKeysymToKeycode)(display, key as _) as c_uint;

                                        if hotkeys.remove(&code).is_some() {
                                            unregister(&xlib, display, window, code);
                                            promise.set(Ok(()));
                                        } else {
                                            promise.set(Err(Error::NotRegistered));
                                        }
                                    }
                                    Message::End => {
                                        break 'event_loop;
                                    }
                                }
                            }
                        } else if mio_event.token() == X_TOKEN {
                            while (xlib.XPending)(display) != 0 {
                                let mut event = mem::MaybeUninit::uninit();
                                (xlib.XNextEvent)(display, event.as_mut_ptr());
                                let event = event.assume_init();
                                if event.get_type() == KeyPress {
                                    let event: &XKeyEvent = event.as_ref();
                                    if let Some(callback) = hotkeys.get_mut(&event.keycode) {
                                        callback();
                                    }
                                }
                            }
                        }
                    }
                }

                for (code, _) in hotkeys {
                    unregister(&xlib, display, window, code);
                }

                (xlib.XCloseDisplay)(display);

                result
            });

            Ok(Hook {
                sender,
                ping,
                _registration: registration,
                join_handle: Some(join_handle),
            })
        }
    }

    pub fn register<F>(&self, hotkey: KeyCode, callback: F) -> Result<()>
    where
        F: FnMut() + Send + 'static,
    {
        let (future, promise) = future_promise();

        self.sender
            .send(Message::Register(hotkey, Box::new(callback), promise))
            .map_err(|_| Error::ThreadStopped)?;

        self.ping
            .set_readiness(Ready::readable())
            .map_err(|_| Error::ThreadStopped)?;

        future.value().ok_or(Error::ThreadStopped)?
    }

    pub fn unregister(&self, hotkey: KeyCode) -> Result<()> {
        let (future, promise) = future_promise();

        self.sender
            .send(Message::Unregister(hotkey, promise))
            .map_err(|_| Error::ThreadStopped)?;
        self.ping
            .set_readiness(Ready::readable())
            .map_err(|_| Error::ThreadStopped)?;

        future.value().ok_or(Error::ThreadStopped)?
    }
}

#[test]
fn test() {
    let hook = Hook::new().unwrap();
    hook.register(KeyCode::NumPad1, || println!("A")).unwrap();
    println!("Press NumPad1");
    thread::sleep(std::time::Duration::from_secs(5));
    hook.unregister(KeyCode::NumPad1).unwrap();
    hook.register(KeyCode::NumPad4, || println!("B")).unwrap();
    println!("Press NumPad4");
    thread::sleep(std::time::Duration::from_secs(5));
    hook.unregister(KeyCode::NumPad4).unwrap();
    hook.register(KeyCode::NumPad1, || println!("C")).unwrap();
    println!("Press NumPad1");
    thread::sleep(std::time::Duration::from_secs(5));
    hook.unregister(KeyCode::NumPad1).unwrap();
}
<commit_msg>replaced the lowlevel register and unregister functions by grab_all and ungrab_all; this fixes the reactivating the same key issue<commit_after>mod key_code;
pub use self::key_code::KeyCode;

use mio::unix::EventedFd;
use mio::{Events, Poll, PollOpt, Ready, Registration, SetReadiness, Token};
use promising_future::{future_promise, Promise};
use std::collections::hash_map::{Entry, HashMap};
use std::os::raw::{c_int, c_uint};
use std::sync::mpsc::{channel, Sender};
use std::thread::{self, JoinHandle};
use std::{mem, ptr};
use x11_dl::xlib::{
    Display, GrabModeAsync, AnyKey, KeyPress, AnyModifier, XErrorEvent, XKeyEvent, Xlib,
};

#[derive(Debug,Copy,Clone, snafu::Snafu)]
pub enum Error {
    NoXLib,
    OpenXServerConnection,
    EPoll,
    ThreadStopped,
    AlreadyRegistered,
    NotRegistered,
}

pub type Result<T> = std::result::Result<T, Error>;

enum Message {
    Register(
        KeyCode,
        Box<dyn FnMut() + Send + 'static>,
        Promise<Result<()>>,
    ),
    Unregister(KeyCode, Promise<Result<()>>),
    End,
}

const X_TOKEN: Token = Token(0);
const PING_TOKEN: Token = Token(1);

pub struct Hook {
    sender: Sender<Message>,
    ping: SetReadiness,
    _registration: Registration,
    join_handle: Option<JoinHandle<Result<()>>>,
}

impl Drop for Hook {
    fn drop(&mut self) {
        self.sender.send(Message::End).ok();
        self.ping.set_readiness(Ready::readable()).ok();
        if let Some(handle) = self.join_handle.take() {
            handle.join().ok();
        }
    }
}

unsafe fn ungrab_all(xlib: &Xlib, display: *mut Display) {
    let screencount = (xlib.XScreenCount)(display);
    for screen in 0..screencount {
        let rootwindow = (xlib.XRootWindow)(display, screen);
        for _i in 0..rootwindow {
            // FIXME: This loop looks very stupid, but it somehow it prevents
            // button presses getting lost.
            (xlib.XUngrabKey)(display, AnyKey, AnyModifier, rootwindow);
        }
    }
}

unsafe fn grab_all(xlib: &Xlib, display: *mut Display, keylist: Vec<c_uint>) {
    ungrab_all(xlib, display);
    let screencount = (xlib.XScreenCount)(display);
    for screen in 0..screencount {
        let rootwindow = (xlib.XRootWindow)(display, screen);
        for code in &keylist {
            (xlib.XGrabKey)(display, *code as _, AnyModifier, rootwindow, false as _, GrabModeAsync, GrabModeAsync);
        }
    }
}

unsafe extern "C" fn handle_error(_: *mut Display, _: *mut XErrorEvent) -> c_int {
    0
}

impl Hook {
    pub fn new() -> Result<Self> {
        unsafe {
            let (sender, receiver) = channel();

            let xlib = Xlib::open().map_err(|_| Error::NoXLib)?;
            (xlib.XSetErrorHandler)(Some(handle_error));

            let display = (xlib.XOpenDisplay)(ptr::null());
            if display.is_null() {
                return Err(Error::OpenXServerConnection);
            }

            let fd = (xlib.XConnectionNumber)(display);
            let poll = Poll::new().map_err(|_| Error::EPoll)?;

            let (registration, ping) = Registration::new2();

            poll.register(
                &EventedFd(&fd),
                X_TOKEN,
                Ready::readable() | Ready::writable(),
                PollOpt::edge(),
            )
            .map_err(|_| Error::EPoll)?;

            poll.register(
                &registration,
                PING_TOKEN,
                Ready::readable(),
                PollOpt::edge(),
            )
            .map_err(|_| Error::EPoll)?;

            struct XData(Xlib, *mut Display);
            unsafe impl Send for XData {}
            let xdata = XData(xlib, display);

            let join_handle = thread::spawn(move || -> Result<()> {
                let XData(xlib, display) = xdata;

                let mut result = Ok(());
                let mut events = Events::with_capacity(1024);
                let mut hotkeys = HashMap::new();

                'event_loop: loop {
                    if poll.poll(&mut events, None).is_err() {
                        result = Err(Error::EPoll);
                        break 'event_loop;
                    }

                    for mio_event in &events {
                        if mio_event.token() == PING_TOKEN {
                            for message in receiver.try_iter() {
                                match message {
                                    Message::Register(key, callback, promise) => {
                                        let code =
                                            (xlib.XKeysymToKeycode)(display, key as _) as c_uint;

                                        if let Entry::Vacant(vacant) = hotkeys.entry(code) {
                                            vacant.insert(callback);
                                            promise.set(Ok(()));
                                        } else {
                                            promise.set(Err(Error::AlreadyRegistered));
                                        }
                                        let keys = hotkeys.keys().map(|x| *x).collect();
                                        grab_all(&xlib, display, keys);
                                    }
                                    Message::Unregister(key, promise) => {
                                        let code = (xlib.XKeysymToKeycode)(display, key as _) as c_uint;

                                        if hotkeys.remove(&code).is_some() {
                                            promise.set(Ok(()));
                                        } else {
                                            promise.set(Err(Error::NotRegistered));
                                        }
                                        let keys = hotkeys.keys().map(|x| *x).collect();
                                        grab_all(&xlib, display, keys);
                                    }
                                    Message::End => {
                                        break 'event_loop;
                                    }
                                }
                            }
                        } else if mio_event.token() == X_TOKEN {
                            while (xlib.XPending)(display) != 0 {
                                let mut event = mem::MaybeUninit::uninit();
                                (xlib.XNextEvent)(display, event.as_mut_ptr());
                                let event = event.assume_init();
                                if event.get_type() == KeyPress {
                                    let event: &XKeyEvent = event.as_ref();
                                    if let Some(callback) = hotkeys.get_mut(&event.keycode) {
                                        callback();
                                    }
                                    // FIXME: We should check else here: these amount to lost
                                    // keypresses.
                                }
                            }
                        }
                    }
                }

                ungrab_all(&xlib, display);

                (xlib.XCloseDisplay)(display);

                result
            });

            Ok(Hook {
                sender,
                ping,
                _registration: registration,
                join_handle: Some(join_handle),
            })
        }
    }

    pub fn register<F>(&self, hotkey: KeyCode, callback: F) -> Result<()>
    where
        F: FnMut() + Send + 'static,
    {
        let (future, promise) = future_promise();

        self.sender
            .send(Message::Register(hotkey, Box::new(callback), promise))
            .map_err(|_| Error::ThreadStopped)?;

        self.ping
            .set_readiness(Ready::readable())
            .map_err(|_| Error::ThreadStopped)?;

        future.value().ok_or(Error::ThreadStopped)?
    }

    pub fn unregister(&self, hotkey: KeyCode) -> Result<()> {
        let (future, promise) = future_promise();

        self.sender
            .send(Message::Unregister(hotkey, promise))
            .map_err(|_| Error::ThreadStopped)?;
        self.ping
            .set_readiness(Ready::readable())
            .map_err(|_| Error::ThreadStopped)?;

        future.value().ok_or(Error::ThreadStopped)?
    }
}

#[test]
fn test() {
    let hook = Hook::new().unwrap();
    hook.register(KeyCode::NumPad1, || println!("A")).unwrap();
    println!("Press NumPad1");
    thread::sleep(std::time::Duration::from_secs(5));
    hook.unregister(KeyCode::NumPad1).unwrap();
    hook.register(KeyCode::NumPad4, || println!("B")).unwrap();
    println!("Press NumPad4");
    thread::sleep(std::time::Duration::from_secs(5));
    hook.unregister(KeyCode::NumPad4).unwrap();
    hook.register(KeyCode::NumPad1, || println!("C")).unwrap();
    println!("Press NumPad1");
    thread::sleep(std::time::Duration::from_secs(5));
    hook.unregister(KeyCode::NumPad1).unwrap();
}
<|endoftext|>


<commit_before>/*
 * Region resolution. This pass runs before typechecking and resolves region
 * names to the appropriate block.
 */

import driver::session::session;
import middle::ty;
import syntax::{ast, visit};
import util::common::new_def_hash;

import std::map;
import std::map::hashmap;

/* Represents the type of the most immediate parent node. */
enum parent {
    pa_item(ast::node_id),
    pa_block(ast::node_id),
    pa_nested_fn(ast::node_id),
    pa_crate
}

type region_map = {
    /*
     * Mapping from blocks and function expression to their parent block or
     * function expression.
     */
    parents: hashmap<ast::node_id,ast::node_id>,
    /* Mapping from a region type in the AST to its resolved region. */
    ast_type_to_region: hashmap<ast::node_id,ty::region>,
    /* Mapping from a local variable to its containing block. */
    local_blocks: hashmap<ast::node_id,ast::node_id>,
    /* Mapping from a region name to its function. */
    region_name_to_fn: hashmap<ast::def_id,ast::node_id>,
};

type ctxt = {
    sess: session,
    def_map: resolve::def_map,
    region_map: @region_map,
    names_in_scope: hashmap<str,ast::def_id>,

    /*
     * A list of local IDs that will be parented to the next block we
     * traverse. This is used when resolving `alt` statements. Since we see
     * the pattern before the associated block, upon seeing a pattern we must
     * parent all the bindings in that pattern to the next block we see.
     */
    mut queued_locals: [ast::node_id],

    parent: parent,

    /* True if we're within the pattern part of an alt, false otherwise. */
    in_alt: bool
};

fn region_to_scope(region_map: @region_map, region: ty::region)
        -> ast::node_id {
    ret alt region {
        ty::re_caller(def_id) { def_id.node }
        ty::re_named(def_id) { region_map.region_name_to_fn.get(def_id) }
        ty::re_block(node_id) { node_id }
    };
}

// Returns true if `subscope` is equal to or is lexically nested inside
// `superscope` and false otherwise.
fn scope_contains(region_map: @region_map, superscope: ast::node_id,
                  subscope: ast::node_id) -> bool {
    let subscope = subscope;
    while superscope != subscope {
        alt region_map.parents.find(subscope) {
            none { ret false; }
            some(scope) { subscope = scope; }
        }
    }
    ret true;
}

fn resolve_ty(ty: @ast::ty, cx: ctxt, visitor: visit::vt<ctxt>) {
    alt ty.node {
        ast::ty_rptr({id: region_id, node: node}, _) {
            let region;
            alt node {
                ast::re_inferred {
                    // We infer to the caller region if we're at item scope
                    // and to the block region if we're at block scope.
                    //
                    // TODO: What do we do if we're in an alt?

                    alt cx.parent {
                        pa_item(item_id) | pa_nested_fn(item_id) {
                            let def_id = {crate: ast::local_crate,
                                          node: item_id};
                            region = ty::re_caller(def_id);
                        }
                        pa_block(block_id) {
                            region = ty::re_block(block_id);
                        }
                        pa_crate {
                            cx.sess.span_bug(ty.span, "inferred region at " +
                                             "crate level?!");
                        }
                    }
                }

                ast::re_named(ident) {
                    // If at item scope, introduce or reuse a binding. If at
                    // block scope, require that the binding be introduced.
                    alt cx.names_in_scope.find(ident) {
                        some(def_id) { region = ty::re_named(def_id); }
                        none {
                            let def_id = {crate: ast::local_crate,
                                          node: region_id};
                            cx.names_in_scope.insert(ident, def_id);
                            region = ty::re_named(def_id);

                            alt cx.parent {
                                pa_item(fn_id) | pa_nested_fn(fn_id) {
                                    let rf = cx.region_map.region_name_to_fn;
                                    rf.insert(def_id, fn_id);
                                }
                                pa_block(_) {
                                    cx.sess.span_err(ty.span,
                                                     "unknown region `" +
                                                     ident + "`");
                                }
                                pa_crate {
                                    cx.sess.span_bug(ty.span, "named " +
                                                     "region at crate " +
                                                     "level?!");
                                }
                            }
                        }
                    }
                }

                ast::re_self {
                    // For blocks, "self" means "the current block".
                    //
                    // TODO: What do we do in an alt?
                    //
                    // FIXME: Doesn't work in type items.

                    alt cx.parent {
                        pa_item(item_id) | pa_nested_fn(item_id) {
                            let def_id = {crate: ast::local_crate,
                                          node: item_id};
                            region = ty::re_caller(def_id);
                        }
                        pa_block(block_id) {
                            region = ty::re_block(block_id);
                        }
                        pa_crate {
                            cx.sess.span_bug(ty.span,
                                             "region type outside item?!");
                        }
                    }
                }

            }

            cx.region_map.ast_type_to_region.insert(region_id, region);
        }
        _ { /* nothing to do */ }
    }

    visit::visit_ty(ty, cx, visitor);
}

fn record_parent(cx: ctxt, child_id: ast::node_id) {
    alt cx.parent {
        pa_item(parent_id) | pa_block(parent_id) | pa_nested_fn(parent_id) {
            cx.region_map.parents.insert(child_id, parent_id);
        }
        pa_crate { /* no-op */ }
    }
}

fn resolve_block(blk: ast::blk, cx: ctxt, visitor: visit::vt<ctxt>) {
    // Record the parent of this block.
    record_parent(cx, blk.node.id);

    // Resolve queued locals to this block.
    for local_id in cx.queued_locals {
        cx.region_map.local_blocks.insert(local_id, blk.node.id);
    }

    // Descend.
    let new_cx: ctxt = {parent: pa_block(blk.node.id),
                        mut queued_locals: [],
                        in_alt: false with cx};
    visit::visit_block(blk, new_cx, visitor);
}

fn resolve_arm(arm: ast::arm, cx: ctxt, visitor: visit::vt<ctxt>) {
    let new_cx: ctxt = {mut queued_locals: [], in_alt: true with cx};
    visit::visit_arm(arm, new_cx, visitor);
}

fn resolve_pat(pat: @ast::pat, cx: ctxt, visitor: visit::vt<ctxt>) {
    alt pat.node {
        ast::pat_ident(path, _) {
            let defn_opt = cx.def_map.find(pat.id);
            alt defn_opt {
                some(ast::def_variant(_,_)) {
                    /* Nothing to do; this names a variant. */
                }
                _ {
                    /*
                     * This names a local. Enqueue it or bind it to the
                     * containing block, depending on whether we're in an alt
                     * or not.
                     */
                    if cx.in_alt {
                        vec::push(cx.queued_locals, pat.id);
                    } else {
                        alt cx.parent {
                            pa_block(block_id) {
                                let local_blocks = cx.region_map.local_blocks;
                                local_blocks.insert(pat.id, block_id);
                            }
                            _ {
                                cx.sess.span_bug(pat.span,
                                                 "unexpected parent");
                            }
                        }
                    }
                }
            }
        }
        _ { /* no-op */ }
    }

    visit::visit_pat(pat, cx, visitor);
}

fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
    alt expr.node {
        ast::expr_fn(_, _, _, _) | ast::expr_fn_block(_, _) {
            record_parent(cx, expr.id);
            let new_cx = {parent: pa_nested_fn(expr.id),
                          in_alt: false with cx};
            visit::visit_expr(expr, new_cx, visitor);
        }
        _ { visit::visit_expr(expr, cx, visitor); }
    }
}

fn resolve_item(item: @ast::item, cx: ctxt, visitor: visit::vt<ctxt>) {
    // Items create a new outer block scope as far as we're concerned.
    let new_cx: ctxt = {names_in_scope: map::new_str_hash(),
                        parent: pa_item(item.id),
                        in_alt: false
                        with cx};
    visit::visit_item(item, new_cx, visitor);
}

fn resolve_crate(sess: session, def_map: resolve::def_map, crate: @ast::crate)
        -> @region_map {
    let cx: ctxt = {sess: sess,
                    def_map: def_map,
                    region_map: @{parents: map::new_int_hash(),
                                  ast_type_to_region: map::new_int_hash(),
                                  local_blocks: map::new_int_hash(),
                                  region_name_to_fn: new_def_hash()},
                    names_in_scope: map::new_str_hash(),
                    mut queued_locals: [],
                    parent: pa_crate,
                    in_alt: false};
    let visitor = visit::mk_vt(@{
        visit_block: resolve_block,
        visit_item: resolve_item,
        visit_ty: resolve_ty,
        visit_arm: resolve_arm,
        visit_pat: resolve_pat,
        visit_expr: resolve_expr
        with *visit::default_visitor()
    });
    visit::visit_crate(*crate, cx, visitor);
    ret cx.region_map;
}

<commit_msg>rustc: Use linked lists instead of hash tables for region binding scopes<commit_after>/*
 * Region resolution. This pass runs before typechecking and resolves region
 * names to the appropriate block.
 */

import driver::session::session;
import middle::ty;
import syntax::{ast, visit};
import util::common::new_def_hash;

import std::list;
import std::list::list;
import std::map;
import std::map::hashmap;

/* Represents the type of the most immediate parent node. */
enum parent {
    pa_item(ast::node_id),
    pa_block(ast::node_id),
    pa_nested_fn(ast::node_id),
    pa_crate
}

/* Records the binding site of a region name. */
type binding = {
    name: str,
    id: ast::def_id
};

type region_map = {
    /*
     * Mapping from blocks and function expression to their parent block or
     * function expression.
     */
    parents: hashmap<ast::node_id,ast::node_id>,
    /* Mapping from a region type in the AST to its resolved region. */
    ast_type_to_region: hashmap<ast::node_id,ty::region>,
    /* Mapping from a local variable to its containing block. */
    local_blocks: hashmap<ast::node_id,ast::node_id>,
    /* Mapping from a region name to its function. */
    region_name_to_fn: hashmap<ast::def_id,ast::node_id>,
};

type ctxt = {
    sess: session,
    def_map: resolve::def_map,
    region_map: @region_map,
    mut bindings: @list<binding>,

    /*
     * A list of local IDs that will be parented to the next block we
     * traverse. This is used when resolving `alt` statements. Since we see
     * the pattern before the associated block, upon seeing a pattern we must
     * parent all the bindings in that pattern to the next block we see.
     */
    mut queued_locals: [ast::node_id],

    parent: parent,

    /* True if we're within the pattern part of an alt, false otherwise. */
    in_alt: bool
};

fn region_to_scope(region_map: @region_map, region: ty::region)
        -> ast::node_id {
    ret alt region {
        ty::re_caller(def_id) { def_id.node }
        ty::re_named(def_id) { region_map.region_name_to_fn.get(def_id) }
        ty::re_block(node_id) { node_id }
    };
}

// Returns true if `subscope` is equal to or is lexically nested inside
// `superscope` and false otherwise.
fn scope_contains(region_map: @region_map, superscope: ast::node_id,
                  subscope: ast::node_id) -> bool {
    let subscope = subscope;
    while superscope != subscope {
        alt region_map.parents.find(subscope) {
            none { ret false; }
            some(scope) { subscope = scope; }
        }
    }
    ret true;
}

fn resolve_ty(ty: @ast::ty, cx: ctxt, visitor: visit::vt<ctxt>) {
    alt ty.node {
        ast::ty_rptr({id: region_id, node: node}, _) {
            let region;
            alt node {
                ast::re_inferred {
                    // We infer to the caller region if we're at item scope
                    // and to the block region if we're at block scope.
                    //
                    // TODO: What do we do if we're in an alt?

                    alt cx.parent {
                        pa_item(item_id) | pa_nested_fn(item_id) {
                            let def_id = {crate: ast::local_crate,
                                          node: item_id};
                            region = ty::re_caller(def_id);
                        }
                        pa_block(block_id) {
                            region = ty::re_block(block_id);
                        }
                        pa_crate {
                            cx.sess.span_bug(ty.span, "inferred region at " +
                                             "crate level?!");
                        }
                    }
                }

                ast::re_named(ident) {
                    // If at item scope, introduce or reuse a binding. If at
                    // block scope, require that the binding be introduced.
                    let bindings = cx.bindings;
                    alt list::find(*bindings, {|b| ident == b.name}) {
                        some(binding) { region = ty::re_named(binding.id); }
                        none {
                            let def_id = {crate: ast::local_crate,
                                          node: region_id};
                            let binding = {name: ident, id: def_id};
                            cx.bindings = @list::cons(binding, cx.bindings);
                            region = ty::re_named(def_id);

                            alt cx.parent {
                                pa_item(fn_id) | pa_nested_fn(fn_id) {
                                    let rf = cx.region_map.region_name_to_fn;
                                    rf.insert(def_id, fn_id);
                                }
                                pa_block(_) {
                                    cx.sess.span_err(ty.span,
                                                     "unknown region `" +
                                                     ident + "`");
                                }
                                pa_crate {
                                    cx.sess.span_bug(ty.span, "named " +
                                                     "region at crate " +
                                                     "level?!");
                                }
                            }
                        }
                    }
                }

                ast::re_self {
                    // For blocks, "self" means "the current block".
                    //
                    // TODO: What do we do in an alt?
                    //
                    // FIXME: Doesn't work in type items.

                    alt cx.parent {
                        pa_item(item_id) | pa_nested_fn(item_id) {
                            let def_id = {crate: ast::local_crate,
                                          node: item_id};
                            region = ty::re_caller(def_id);
                        }
                        pa_block(block_id) {
                            region = ty::re_block(block_id);
                        }
                        pa_crate {
                            cx.sess.span_bug(ty.span,
                                             "region type outside item?!");
                        }
                    }
                }

            }

            cx.region_map.ast_type_to_region.insert(region_id, region);
        }
        _ { /* nothing to do */ }
    }

    visit::visit_ty(ty, cx, visitor);
}

fn record_parent(cx: ctxt, child_id: ast::node_id) {
    alt cx.parent {
        pa_item(parent_id) | pa_block(parent_id) | pa_nested_fn(parent_id) {
            cx.region_map.parents.insert(child_id, parent_id);
        }
        pa_crate { /* no-op */ }
    }
}

fn resolve_block(blk: ast::blk, cx: ctxt, visitor: visit::vt<ctxt>) {
    // Record the parent of this block.
    record_parent(cx, blk.node.id);

    // Resolve queued locals to this block.
    for local_id in cx.queued_locals {
        cx.region_map.local_blocks.insert(local_id, blk.node.id);
    }

    // Descend.
    let new_cx: ctxt = {parent: pa_block(blk.node.id),
                        mut queued_locals: [],
                        in_alt: false with cx};
    visit::visit_block(blk, new_cx, visitor);
}

fn resolve_arm(arm: ast::arm, cx: ctxt, visitor: visit::vt<ctxt>) {
    let new_cx: ctxt = {mut queued_locals: [], in_alt: true with cx};
    visit::visit_arm(arm, new_cx, visitor);
}

fn resolve_pat(pat: @ast::pat, cx: ctxt, visitor: visit::vt<ctxt>) {
    alt pat.node {
        ast::pat_ident(path, _) {
            let defn_opt = cx.def_map.find(pat.id);
            alt defn_opt {
                some(ast::def_variant(_,_)) {
                    /* Nothing to do; this names a variant. */
                }
                _ {
                    /*
                     * This names a local. Enqueue it or bind it to the
                     * containing block, depending on whether we're in an alt
                     * or not.
                     */
                    if cx.in_alt {
                        vec::push(cx.queued_locals, pat.id);
                    } else {
                        alt cx.parent {
                            pa_block(block_id) {
                                let local_blocks = cx.region_map.local_blocks;
                                local_blocks.insert(pat.id, block_id);
                            }
                            _ {
                                cx.sess.span_bug(pat.span,
                                                 "unexpected parent");
                            }
                        }
                    }
                }
            }
        }
        _ { /* no-op */ }
    }

    visit::visit_pat(pat, cx, visitor);
}

fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
    alt expr.node {
        ast::expr_fn(_, _, _, _) | ast::expr_fn_block(_, _) {
            record_parent(cx, expr.id);
            let new_cx = {parent: pa_nested_fn(expr.id),
                          in_alt: false with cx};
            visit::visit_expr(expr, new_cx, visitor);
        }
        _ { visit::visit_expr(expr, cx, visitor); }
    }
}

fn resolve_item(item: @ast::item, cx: ctxt, visitor: visit::vt<ctxt>) {
    // Items create a new outer block scope as far as we're concerned.
    let new_cx: ctxt = {bindings: @list::nil,
                        parent: pa_item(item.id),
                        in_alt: false
                        with cx};
    visit::visit_item(item, new_cx, visitor);
}

fn resolve_crate(sess: session, def_map: resolve::def_map, crate: @ast::crate)
        -> @region_map {
    let cx: ctxt = {sess: sess,
                    def_map: def_map,
                    region_map: @{parents: map::new_int_hash(),
                                  ast_type_to_region: map::new_int_hash(),
                                  local_blocks: map::new_int_hash(),
                                  region_name_to_fn: new_def_hash()},
                    mut bindings: @list::nil,
                    mut queued_locals: [],
                    parent: pa_crate,
                    in_alt: false};
    let visitor = visit::mk_vt(@{
        visit_block: resolve_block,
        visit_item: resolve_item,
        visit_ty: resolve_ty,
        visit_arm: resolve_arm,
        visit_pat: resolve_pat,
        visit_expr: resolve_expr
        with *visit::default_visitor()
    });
    visit::visit_crate(*crate, cx, visitor);
    ret cx.region_map;
}

<|endoftext|>


<commit_before>//! This crate helps to create a Fractalide agent
//!
//! It provides the macro `agent` which takes the high level view of the agent, and creates the code for the scheduler.
//!
//! It also declare the Trait Agent and the shared methods needed by every agent and the scheduler.

extern crate capnp;

// TODO : Add method to remove agents
use ports::{MsgSender, MsgReceiver};
use scheduler::Signal;
use result::Result;

/// Provide the generic functions of agents
///
/// These three functions are used by the scheduler
pub trait Agent {
    /// Return true if there is at least one input port
    fn is_input_ports(&self) -> bool;
    /// Connect output port
    fn connect(&mut self, port: &str, sender: MsgSender) -> Result<()>;
    /// Connect array output port
    fn connect_array(&mut self, port: &str, element: String, sender: MsgSender) -> Result<()>;
    /// Add input element
    fn add_inarr_element(&mut self, port: &str, element: String, recv: MsgReceiver) -> Result<()>;
    /// Run the method of the agent, his personal logic
    fn run(&mut self) -> Result<Signal>;
}

/// The agent macro.
///
/// It helps to define a agent, by defining the input and output ports, if there is an option or an acc port, ...
///
/// Example :
///
/// ```rust,ignore
/// agent! {
///    inputs(input: any),
///    outputs(output: any),
///    option(generic_text),
///    fn run(&mut self) -> Result<Signal> {
///        // Receive an IP
///        let msg = try!(self.input.input.recv());
///
///        // Received an IP from the option port (a generic_text)
///        let opt = self.recv_opt();
///
///        // Get the capn'p reader
///        let reader: generic_text::Reader = try!(opt.read_schema());
///        // Print the option
///        println!("{}", try!(reader.get_text()));
///
///        // Send the received IP outside, but don't care about the success (drop on fail)
///        let _ = self.output.output.send(msg);
///
///        Ok(End)
///    }
/// }
/// ```
#[macro_export]
macro_rules! agent {
    (
        $( input($( $input_name:ident: $input_contract:ident ),*), )*
        $( inarr($( $input_a_name:ident: $input_a_contract:ident ),*), )*
        $( output($( $output_name:ident: $output_contract:ident ),*), )*
        $( outarr($( $output_a_name:ident: $output_a_contract:ident ),*), )*
        $( portal( $portal_type:ty => $portal_value:expr ), )*
        $( option($option:ident), )*
        $( accumulator($acc:ident ), )*
        fn run(&mut $arg:ident) -> Result<Signal> $fun:block
    )
        =>
    {
        use rustfbp::agent::Agent;

        use rustfbp::result;
        use rustfbp::result::Result;
        use rustfbp::scheduler::{CompMsg, Signal};
        use std::error::Error;

        use std::sync::mpsc::{Sender};
        use std::sync::mpsc::channel;

        use rustfbp::ports::{Msg, MsgSender, MsgReceiver, OutputSend};

        #[allow(unused_imports)]
        use std::collections::HashMap;

        use capnp::serialize;
        use capnp::message;

        use std::io::{Read, Write};

        use rustfbp::scheduler::Signal::*;

        mod edge_capnp {
                include!("edge_capnp.rs");
        }

        use edge_capnp::*;

        impl ThisAgent {
            $(
            fn dummy() {
                if stringify!($option) != "" {}
            }
            pub fn recv_option(&mut self) -> Msg {
                self.try_recv_option();
                if self.option_msg.is_none() {
                    self.option_msg = self.input.option.recv().ok();
                }
                match self.option_msg {
                    Some(ref msg) => msg.clone(),
                    None => unreachable!(),
                }
            }

            pub fn try_recv_option(&mut self) -> Option<Msg> {
                loop {
                    match self.input.option.try_recv() {
                        Err(_) => { break; },
                        Ok(msg) => { self.option_msg = Some(msg); }
                    };
                }
                self.option_msg.as_ref().map(|msg|{ msg.clone() })
            }
            )*

            pub fn send_action(&mut self, output: &str, msg: Msg) -> Result<()> {
                if let Some(sender) = {
                    match output {
                        $($(
                            stringify!($output_a_name) =>  { self.outarr.$output_a_name.get(&msg.action) }
                        )*)*
                            _ => None
                    }
                } // End of the if let Some = { ... }
                {
                    let s: &MsgSender = sender;
                    try!(s.send(msg));
                }
                else {
                    match output {
                        $($(
                            stringify!($output_name) => { self.output.$output_name.send(msg);}
                        )*)*
                            _ => { return Err(result::Error::PortDontExist(output.into())); }
                    }
                }
                Ok(())
            }
        }

        impl Agent for ThisAgent {

            fn is_input_ports(&self) -> bool {
                $($(
                    if true || stringify!($input_name) == "" { return true; }
                )*)*
                $($(
                    if true || stringify!($input_a_name) == "" { return true; }
                )*)*
                false
            }

            fn connect(&mut self, port: &str, sender: MsgSender) -> Result<()> {
                match port {
                    $($(
                        stringify!($output_name) => {
                            self.output.$output_name = Some(sender);
                        }
                    )*)*
                        _ => {
                            return Err(result::Error::PortDontExist(port.into()));
                        }
                }
                Ok(())
            }

            fn connect_array(&mut self, port: &str, element: String, sender: MsgSender) -> Result<()> {
                match port {
                    $($(
                        stringify!($output_a_name) => {
                            self.outarr.$output_a_name.insert(element, sender);
                        }
                    )*)*
                        _ => {
                            return Err(result::Error::PortDontExist(port.into()));
                        }
                }
                Ok(())
            }

            fn add_inarr_element(&mut self, port: &str, element: String, recv: MsgReceiver) -> Result<()> {
                match port {
                    $($(
                        stringify!($input_a_name) => {
                            self.inarr.$input_a_name.insert(element, recv);
                            Ok(())
                        }
                    )*)*
                        _ => {
                            Err(result::Error::PortDontExist(port.into()))
                        }
                }
            }

            fn run(&mut $arg) -> Result<Signal> $fun

        }

        pub struct Input {
            option: MsgReceiver,
            acc: MsgReceiver,
            $($(
                $input_name: MsgReceiver,
            )*)*
        }

        pub struct Inarr {
            $($(
                $input_a_name: HashMap<String, MsgReceiver>,
            )*)*
        }

        pub struct Output {
            acc: Option<MsgSender>,
            $($(
                $output_name: Option<MsgSender>,
            )*)*
        }

        pub struct Outarr {
            $($(
                $output_a_name: HashMap<String, MsgSender>
            )*)*
        }

        /* Global agent */

        #[allow(dead_code)]
        #[allow(non_camel_case_types)]
        pub struct ThisAgent {
            name: String,
            pub input: Input,
            pub inarr: Inarr,
            pub output: Output,
            pub outarr: Outarr,
            pub option_msg: Option<Msg>,
            sched: Sender<CompMsg>,
            $(
            pub portal: $portal_type ,
            )*
        }

        #[allow(dead_code)]
        pub fn new(name: String, sched: Sender<CompMsg>) -> Result<(Box<Agent + Send>, HashMap<String, MsgSender>)> {

            let mut senders: HashMap<String, MsgSender> = HashMap::new();
            let option = MsgReceiver::new(name.clone(), sched.clone(), false);
            senders.insert("option".to_string(), option.1);
            let acc = MsgReceiver::new(name.clone(), sched.clone(), false);
            senders.insert("acc".to_string(), acc.1.clone());
            $($(
                let $input_name = MsgReceiver::new(name.clone(), sched.clone(), true);
                senders.insert(stringify!($input_name).to_string(), $input_name.1);
            )*)*
            let input = Input {
                option: option.0,
                acc: acc.0,
                $($(
                    $input_name: $input_name.0,
                )*)*
            };

            let inarr = Inarr {
                $($(
                    $input_a_name: HashMap::new(),
                )*)*
            };

            let output = Output {
                acc: Some(acc.1),
                $($(
                    $output_name: None,
                )*)*
            };

            let outarr = Outarr {
                $($(
                    $output_a_name: HashMap::new(),
                )*)*
            };

            let agent= ThisAgent {
                name: name,
                input: input,
                inarr: inarr,
                output: output,
                outarr: outarr,
                option_msg: None,
                sched: sched,
                $(
                    portal: $portal_value,
                )*
            };

            Ok((Box::new(agent) as Box<Agent + Send>, senders))
        }

        #[no_mangle]
        pub extern fn create_agent(name: String, sched: Sender<CompMsg>) -> Result<(Box<Agent + Send>, HashMap<String, MsgSender>)> {
            new(name, sched)
        }

        #[no_mangle]
        pub extern fn get_schema_input(port: &str) -> Result<String> {
            match port {
                $($(
                    stringify!($input_name)=> Ok(stringify!($input_contract).into()),
                )*)*
                $(
                    "option" => Ok(stringify!($option).into()),
                )*
                $(
                    "acc" => Ok(stringify!($acc).into()),
                )*
                _ => { Err(result::Error::PortDontExist(port.into())) }
            }
        }

        #[no_mangle]
        pub extern fn get_schema_input_array(port: &str) -> Result<String> {
            match port {
                $($(
                    stringify!($input_a_name) => Ok(stringify!($input_a_contract).into()),
                )*)*
                _ => { Err(result::Error::PortDontExist(port.into())) }
            }
        }

        #[no_mangle]
        pub extern fn get_schema_output(port: &str) -> Result<String> {
            match port {
                $($(
                    stringify!($output_name)=> Ok(stringify!($output_contract).into()),
                )*)*
                _ => { Err(result::Error::PortDontExist(port.into())) }
            }
        }

        #[no_mangle]
        pub extern fn get_schema_output_array(port: &str) -> Result<String> {
            match port {
                $($(
                    stringify!($output_a_name) => Ok(stringify!($output_a_contract).into()),
                )*)*
                _ => { Err(result::Error::PortDontExist(port.into())) }
            }
        }
    }
}
<commit_msg>[FIX] mutliple outarr doesn't work<commit_after>//! This crate helps to create a Fractalide agent
//!
//! It provides the macro `agent` which takes the high level view of the agent, and creates the code for the scheduler.
//!
//! It also declare the Trait Agent and the shared methods needed by every agent and the scheduler.

extern crate capnp;

// TODO : Add method to remove agents
use ports::{MsgSender, MsgReceiver};
use scheduler::Signal;
use result::Result;

/// Provide the generic functions of agents
///
/// These three functions are used by the scheduler
pub trait Agent {
    /// Return true if there is at least one input port
    fn is_input_ports(&self) -> bool;
    /// Connect output port
    fn connect(&mut self, port: &str, sender: MsgSender) -> Result<()>;
    /// Connect array output port
    fn connect_array(&mut self, port: &str, element: String, sender: MsgSender) -> Result<()>;
    /// Add input element
    fn add_inarr_element(&mut self, port: &str, element: String, recv: MsgReceiver) -> Result<()>;
    /// Run the method of the agent, his personal logic
    fn run(&mut self) -> Result<Signal>;
}

/// The agent macro.
///
/// It helps to define a agent, by defining the input and output ports, if there is an option or an acc port, ...
///
/// Example :
///
/// ```rust,ignore
/// agent! {
///    inputs(input: any),
///    outputs(output: any),
///    option(generic_text),
///    fn run(&mut self) -> Result<Signal> {
///        // Receive an IP
///        let msg = try!(self.input.input.recv());
///
///        // Received an IP from the option port (a generic_text)
///        let opt = self.recv_opt();
///
///        // Get the capn'p reader
///        let reader: generic_text::Reader = try!(opt.read_schema());
///        // Print the option
///        println!("{}", try!(reader.get_text()));
///
///        // Send the received IP outside, but don't care about the success (drop on fail)
///        let _ = self.output.output.send(msg);
///
///        Ok(End)
///    }
/// }
/// ```
#[macro_export]
macro_rules! agent {
    (
        $( input($( $input_name:ident: $input_contract:ident ),*), )*
        $( inarr($( $input_a_name:ident: $input_a_contract:ident ),*), )*
        $( output($( $output_name:ident: $output_contract:ident ),*), )*
        $( outarr($( $output_a_name:ident: $output_a_contract:ident ),*), )*
        $( portal( $portal_type:ty => $portal_value:expr ), )*
        $( option($option:ident), )*
        $( accumulator($acc:ident ), )*
        fn run(&mut $arg:ident) -> Result<Signal> $fun:block
    )
        =>
    {
        use rustfbp::agent::Agent;

        use rustfbp::result;
        use rustfbp::result::Result;
        use rustfbp::scheduler::{CompMsg, Signal};
        use std::error::Error;

        use std::sync::mpsc::{Sender};
        use std::sync::mpsc::channel;

        use rustfbp::ports::{Msg, MsgSender, MsgReceiver, OutputSend};

        #[allow(unused_imports)]
        use std::collections::HashMap;

        use capnp::serialize;
        use capnp::message;

        use std::io::{Read, Write};

        use rustfbp::scheduler::Signal::*;

        mod edge_capnp {
                include!("edge_capnp.rs");
        }

        use edge_capnp::*;

        impl ThisAgent {
            $(
            fn dummy() {
                if stringify!($option) != "" {}
            }
            pub fn recv_option(&mut self) -> Msg {
                self.try_recv_option();
                if self.option_msg.is_none() {
                    self.option_msg = self.input.option.recv().ok();
                }
                match self.option_msg {
                    Some(ref msg) => msg.clone(),
                    None => unreachable!(),
                }
            }

            pub fn try_recv_option(&mut self) -> Option<Msg> {
                loop {
                    match self.input.option.try_recv() {
                        Err(_) => { break; },
                        Ok(msg) => { self.option_msg = Some(msg); }
                    };
                }
                self.option_msg.as_ref().map(|msg|{ msg.clone() })
            }
            )*

            pub fn send_action(&mut self, output: &str, msg: Msg) -> Result<()> {
                if let Some(sender) = {
                    match output {
                        $($(
                            stringify!($output_a_name) =>  { self.outarr.$output_a_name.get(&msg.action) }
                        )*)*
                            _ => None
                    }
                } // End of the if let Some = { ... }
                {
                    let s: &MsgSender = sender;
                    try!(s.send(msg));
                }
                else {
                    match output {
                        $($(
                            stringify!($output_name) => { self.output.$output_name.send(msg);}
                        )*)*
                            _ => { return Err(result::Error::PortDontExist(output.into())); }
                    }
                }
                Ok(())
            }
        }

        impl Agent for ThisAgent {

            fn is_input_ports(&self) -> bool {
                $($(
                    if true || stringify!($input_name) == "" { return true; }
                )*)*
                $($(
                    if true || stringify!($input_a_name) == "" { return true; }
                )*)*
                false
            }

            fn connect(&mut self, port: &str, sender: MsgSender) -> Result<()> {
                match port {
                    $($(
                        stringify!($output_name) => {
                            self.output.$output_name = Some(sender);
                        }
                    )*)*
                        _ => {
                            return Err(result::Error::PortDontExist(port.into()));
                        }
                }
                Ok(())
            }

            fn connect_array(&mut self, port: &str, element: String, sender: MsgSender) -> Result<()> {
                match port {
                    $($(
                        stringify!($output_a_name) => {
                            self.outarr.$output_a_name.insert(element, sender);
                        }
                    )*)*
                        _ => {
                            return Err(result::Error::PortDontExist(port.into()));
                        }
                }
                Ok(())
            }

            fn add_inarr_element(&mut self, port: &str, element: String, recv: MsgReceiver) -> Result<()> {
                match port {
                    $($(
                        stringify!($input_a_name) => {
                            self.inarr.$input_a_name.insert(element, recv);
                            Ok(())
                        }
                    )*)*
                        _ => {
                            Err(result::Error::PortDontExist(port.into()))
                        }
                }
            }

            fn run(&mut $arg) -> Result<Signal> $fun

        }

        pub struct Input {
            option: MsgReceiver,
            acc: MsgReceiver,
            $($(
                $input_name: MsgReceiver,
            )*)*
        }

        pub struct Inarr {
            $($(
                $input_a_name: HashMap<String, MsgReceiver>,
            )*)*
        }

        pub struct Output {
            acc: Option<MsgSender>,
            $($(
                $output_name: Option<MsgSender>,
            )*)*
        }

        pub struct Outarr {
            $($(
                $output_a_name: HashMap<String, MsgSender>,
            )*)*
        }

        /* Global agent */

        #[allow(dead_code)]
        #[allow(non_camel_case_types)]
        pub struct ThisAgent {
            name: String,
            pub input: Input,
            pub inarr: Inarr,
            pub output: Output,
            pub outarr: Outarr,
            pub option_msg: Option<Msg>,
            sched: Sender<CompMsg>,
            $(
            pub portal: $portal_type ,
            )*
        }

        #[allow(dead_code)]
        pub fn new(name: String, sched: Sender<CompMsg>) -> Result<(Box<Agent + Send>, HashMap<String, MsgSender>)> {

            let mut senders: HashMap<String, MsgSender> = HashMap::new();
            let option = MsgReceiver::new(name.clone(), sched.clone(), false);
            senders.insert("option".to_string(), option.1);
            let acc = MsgReceiver::new(name.clone(), sched.clone(), false);
            senders.insert("acc".to_string(), acc.1.clone());
            $($(
                let $input_name = MsgReceiver::new(name.clone(), sched.clone(), true);
                senders.insert(stringify!($input_name).to_string(), $input_name.1);
            )*)*
            let input = Input {
                option: option.0,
                acc: acc.0,
                $($(
                    $input_name: $input_name.0,
                )*)*
            };

            let inarr = Inarr {
                $($(
                    $input_a_name: HashMap::new(),
                )*)*
            };

            let output = Output {
                acc: Some(acc.1),
                $($(
                    $output_name: None,
                )*)*
            };

            let outarr = Outarr {
                $($(
                    $output_a_name: HashMap::new(),
                )*)*
            };

            let agent= ThisAgent {
                name: name,
                input: input,
                inarr: inarr,
                output: output,
                outarr: outarr,
                option_msg: None,
                sched: sched,
                $(
                    portal: $portal_value,
                )*
            };

            Ok((Box::new(agent) as Box<Agent + Send>, senders))
        }

        #[no_mangle]
        pub extern fn create_agent(name: String, sched: Sender<CompMsg>) -> Result<(Box<Agent + Send>, HashMap<String, MsgSender>)> {
            new(name, sched)
        }

        #[no_mangle]
        pub extern fn get_schema_input(port: &str) -> Result<String> {
            match port {
                $($(
                    stringify!($input_name)=> Ok(stringify!($input_contract).into()),
                )*)*
                $(
                    "option" => Ok(stringify!($option).into()),
                )*
                $(
                    "acc" => Ok(stringify!($acc).into()),
                )*
                _ => { Err(result::Error::PortDontExist(port.into())) }
            }
        }

        #[no_mangle]
        pub extern fn get_schema_input_array(port: &str) -> Result<String> {
            match port {
                $($(
                    stringify!($input_a_name) => Ok(stringify!($input_a_contract).into()),
                )*)*
                _ => { Err(result::Error::PortDontExist(port.into())) }
            }
        }

        #[no_mangle]
        pub extern fn get_schema_output(port: &str) -> Result<String> {
            match port {
                $($(
                    stringify!($output_name)=> Ok(stringify!($output_contract).into()),
                )*)*
                _ => { Err(result::Error::PortDontExist(port.into())) }
            }
        }

        #[no_mangle]
        pub extern fn get_schema_output_array(port: &str) -> Result<String> {
            match port {
                $($(
                    stringify!($output_a_name) => Ok(stringify!($output_a_contract).into()),
                )*)*
                _ => { Err(result::Error::PortDontExist(port.into())) }
            }
        }
    }
}
<|endoftext|>