<commit_before>#[cfg(feature = "inline_asm")]
fn main() {}

#[cfg(all(not(feature = "inline_asm"), not(feature = "stable")))]
fn check_inline_asm() {
    compile_error!("Neither feature \"stable\" nor \"inline_asm\" was set!");
}

#[cfg(all(not(feature = "inline_asm"), feature = "stable"))]
fn main() {
    use std::ffi::OsString;
    use std::fs;

    println!("cargo:rerun-if-changed=build.rs");

    let entries = fs::read_dir("src/asm")
        .unwrap()
        .filter_map(|f| {
            f.ok().and_then(|e| {
                let path = e.path();
                match path.extension() {
                    Some(ext) if ext.eq(&OsString::from("s")) => Some(path),
                    _ => None,
                }
            })
        })
        .collect::<Vec<_>>();

    cc::Build::new()
        .no_default_flags(true)
        .files(&entries)
        .pic(true)
        .static_flag(true)
        .shared_flag(false)
        .compile("x86_64_asm");

    for e in entries {
        println!("cargo:rerun-if-changed={}", e.to_str().unwrap());
    }
}
<commit_msg>Improve error message with `--no-default-features`<commit_after>#[cfg(feature = "inline_asm")]
fn main() {}

#[cfg(all(not(feature = "inline_asm"), not(feature = "stable")))]
fn main() {
    compile_error!("Neither feature \"stable\" nor \"inline_asm\" was set!");
}

#[cfg(all(not(feature = "inline_asm"), feature = "stable"))]
fn main() {
    use std::ffi::OsString;
    use std::fs;

    println!("cargo:rerun-if-changed=build.rs");

    let entries = fs::read_dir("src/asm")
        .unwrap()
        .filter_map(|f| {
            f.ok().and_then(|e| {
                let path = e.path();
                match path.extension() {
                    Some(ext) if ext.eq(&OsString::from("s")) => Some(path),
                    _ => None,
                }
            })
        })
        .collect::<Vec<_>>();

    cc::Build::new()
        .no_default_flags(true)
        .files(&entries)
        .pic(true)
        .static_flag(true)
        .shared_flag(false)
        .compile("x86_64_asm");

    for e in entries {
        println!("cargo:rerun-if-changed={}", e.to_str().unwrap());
    }
}
<|endoftext|>


<commit_before>extern crate cmake;
extern crate pkg_config;

use cmake::Config;
use std::env;
use std::path::PathBuf;

fn main() {
    match pkg_config::find_library("heartbeats-simple") {
        Ok(_) => (),
        Err(_) => {
            let src = PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/heartbeats-simple"));
            let mut config = Config::new(&src);

            // check for Android
            let target: String = env::var("TARGET").unwrap();
            let target_parts: Vec<&str> = target.split('-').collect();
            if target_parts[target_parts.len() - 1].starts_with("android") {
                config.define("CMAKE_TOOLCHAIN_FILE",
                              src.join("cmake-toolchain").join("android.toolchain.cmake"));
                // the cmake toolchain above has some specific ideas of what
                // needs to be set
                env::set_var("ANDROID_STANDALONE_TOOLCHAIN", env::var("ANDROID_TOOLCHAIN").unwrap());
                env::remove_var("ANDROID_NDK");
            }

            // build/install everything - the build is fast and portable enough
            // cmake crate makes it too troublesome to do individual targets since it expects to install
            let dst: PathBuf = config.build();
            // none of the libraries have transitive dependencies
            println!("cargo:rustc-link-lib=static=heartbeats-simple");
            println!("cargo:rustc-link-search=native={}", dst.join("lib").display());
        },
    }
}
<commit_msg>Remove Android-specific code<commit_after>extern crate cmake;
extern crate pkg_config;

use cmake::Config;
use std::env;
use std::path::PathBuf;

fn main() {
    match pkg_config::find_library("heartbeats-simple") {
        Ok(_) => (),
        Err(_) => {
            let src = PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/heartbeats-simple"));
            let mut config = Config::new(&src);

            // build/install everything - the build is fast and portable enough
            // cmake crate makes it too troublesome to do individual targets since it expects to install
            let dst: PathBuf = config.build();
            // none of the libraries have transitive dependencies
            println!("cargo:rustc-link-lib=static=heartbeats-simple");
            println!("cargo:rustc-link-search=native={}", dst.join("lib").display());
        },
    }
}
<|endoftext|>


<commit_before>mod target {
    use std::env;
    use std::fs::File;
    use std::io::Write;
    use std::path::{Path, PathBuf};

    pub fn main() {
        let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());

        let mut dst =
            File::create(Path::new(&out_dir).join("host-target.txt")).unwrap();
        dst.write_all(env::var("TARGET").unwrap().as_bytes())
            .unwrap();
    }
}

mod testgen {
    use std::char;
    use std::env;
    use std::ffi::OsStr;
    use std::fs::{self, File};
    use std::io::Write;
    use std::path::{Path, PathBuf};

    pub fn main() {
        let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
        let mut dst =
            File::create(Path::new(&out_dir).join("tests.rs")).unwrap();

        let manifest_dir =
            PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
        let headers_dir = manifest_dir.join("tests").join("headers");

        let headers = match fs::read_dir(headers_dir) {
            Ok(dir) => dir,
            // We may not have headers directory after packaging.
            Err(..) => return,
        };

        let entries =
            headers.map(|result| result.expect("Couldn't read header file"));

        println!("cargo:rerun-if-changed=tests/headers");

        // On behalf of clang_sys, rebuild ourselves if important configuration
        // variables change, to ensure that bindings get rebuilt if the
        // underlying libclang changes.
        println!("cargo:rerun-if-env-changed=LLVM_CONFIG_PATH");
        println!("cargo:rerun-if-env-changed=LIBCLANG_PATH");
        println!("cargo:rerun-if-env-changed=LIBCLANG_STATIC_PATH");

        for entry in entries {
            match entry.path().extension().and_then(OsStr::to_str) {
                Some("h") | Some("hpp") => {
                    let func = entry
                        .file_name()
                        .to_str()
                        .unwrap()
                        .replace(|c| !char::is_alphanumeric(c), "_")
                        .replace("__", "_")
                        .to_lowercase();
                    writeln!(
                        dst,
                        "test_header!(header_{}, {:?});",
                        func,
                        entry.path(),
                    )
                    .unwrap();
                }
                _ => {}
            }
        }

        dst.flush().unwrap();
    }
}

fn main() {
    target::main();
    testgen::main();
}
<commit_msg>build: move the rebuild dependencies to somewhere where they don't seem test specific.<commit_after>mod target {
    use std::env;
    use std::fs::File;
    use std::io::Write;
    use std::path::{Path, PathBuf};

    pub fn main() {
        let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());

        let mut dst =
            File::create(Path::new(&out_dir).join("host-target.txt")).unwrap();
        dst.write_all(env::var("TARGET").unwrap().as_bytes())
            .unwrap();
    }
}

mod testgen {
    use std::char;
    use std::env;
    use std::ffi::OsStr;
    use std::fs::{self, File};
    use std::io::Write;
    use std::path::{Path, PathBuf};

    pub fn main() {
        let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
        let mut dst =
            File::create(Path::new(&out_dir).join("tests.rs")).unwrap();

        let manifest_dir =
            PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
        let headers_dir = manifest_dir.join("tests").join("headers");

        let headers = match fs::read_dir(headers_dir) {
            Ok(dir) => dir,
            // We may not have headers directory after packaging.
            Err(..) => return,
        };

        let entries =
            headers.map(|result| result.expect("Couldn't read header file"));

        println!("cargo:rerun-if-changed=tests/headers");

        for entry in entries {
            match entry.path().extension().and_then(OsStr::to_str) {
                Some("h") | Some("hpp") => {
                    let func = entry
                        .file_name()
                        .to_str()
                        .unwrap()
                        .replace(|c| !char::is_alphanumeric(c), "_")
                        .replace("__", "_")
                        .to_lowercase();
                    writeln!(
                        dst,
                        "test_header!(header_{}, {:?});",
                        func,
                        entry.path(),
                    )
                    .unwrap();
                }
                _ => {}
            }
        }

        dst.flush().unwrap();
    }
}

fn main() {
    target::main();
    testgen::main();

    // On behalf of clang_sys, rebuild ourselves if important configuration
    // variables change, to ensure that bindings get rebuilt if the
    // underlying libclang changes.
    println!("cargo:rerun-if-env-changed=LLVM_CONFIG_PATH");
    println!("cargo:rerun-if-env-changed=LIBCLANG_PATH");
    println!("cargo:rerun-if-env-changed=LIBCLANG_STATIC_PATH");
}
<|endoftext|>


<commit_before>extern crate gcc;

use std::path::PathBuf;
use std::env;

const LIB_NAME: &'static str = "libctxswtch.a";

fn main() {
    let target = env::var("TARGET").unwrap();
    let arch_sub = target.split('-').next().unwrap();

    let arch =
        match arch_sub {
            "x86_64" => "x86_64",
            "x86" => "i686",
            "arm" => "arm",
            "mips" => "mips",
            "mipsel" => "mipsel",
            _ => {
                panic!("Unsupported architecture: {}", target);
            }
        };

    let src_path = &["src", "asm", arch, "_context.S"].iter().collect::<PathBuf>();
    gcc::compile_library(LIB_NAME, &[src_path.to_str().unwrap()]);

// seems like this line is no need actually
//    println!("cargo:rustc-flags=-l ctxswtch:static");
}
<commit_msg>[#22] add i*86 triple arch<commit_after>extern crate gcc;

use std::path::PathBuf;
use std::env;

const LIB_NAME: &'static str = "libctxswtch.a";

fn main() {
    let target = env::var("TARGET").unwrap();
    let arch_sub = target.split('-').next().unwrap();

    let arch =
        match arch_sub {
            "x86_64" => "x86_64",
            "x86" | "i686" | "i586" | "i486" | "i386" => "i686",
            "arm" => "arm",
            "mips" => "mips",
            "mipsel" => "mipsel",
            _ => {
                panic!("Unsupported architecture: {}", target);
            }
        };

    let src_path = &["src", "asm", arch, "_context.S"].iter().collect::<PathBuf>();
    gcc::compile_library(LIB_NAME, &[src_path.to_str().unwrap()]);

// seems like this line is no need actually
//    println!("cargo:rustc-flags=-l ctxswtch:static");
}
<|endoftext|>


<commit_before>use std::os;
use std::io::Command;
use std::io::process::InheritFd;

fn main() {
    let dst = Path::new(os::getenv("OUT_DIR").unwrap());

    setup();
    configure();
    build();
    install(&dst);
    clean();
    println!("cargo:rustc-flags=-L {} -l termbox:static", dst.join("lib").display());
}

fn setup() {
    let mut cmd = Command::new("git");
    cmd.arg("clone");
    cmd.arg("https://github.com/nsf/termbox");
    cmd.arg(".termbox");
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    cmd.cwd(&cargo_dir);

    run(&mut cmd);
}

fn clean() {
    let mut cmd = Command::new("rm");
    cmd.arg("-rf");
    cmd.arg(".termbox");
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    cmd.cwd(&cargo_dir);
    run(&mut cmd);
}

fn configure() {
    let mut cmd = waf();
    cmd.arg("configure");
    cmd.arg("--prefix=/");

    let target = os::getenv("TARGET").unwrap();
    let mut cflags;
    if target.as_slice().contains("i686") {
        cflags = "-m32"
    } else if target.as_slice().contains("x86_64") {
        cflags = "-m64 -fPIC"
    } else {
        cflags = ""
    }
    println!("waf configure: setting CFLAGS to: `{}`", cflags);
    os::setenv("CFLAGS", cflags);

    run(&mut cmd);
    os::unsetenv("CFLAGS");
}

fn build() {
    let mut cmd = waf();
    cmd.arg("build");
    cmd.arg("--targets=termbox_static");
    run(&mut cmd);
}

fn install(dst: &Path) {
    let mut cmd = waf();
    cmd.arg("install");
    cmd.arg("--targets=termbox_static");
    cmd.arg(format!("--destdir={}", dst.display()));
    run(&mut cmd);
}

fn waf() -> Command {
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    let termbox_dir = cargo_dir.join(".termbox");
    let mut cmd = Command::new("./waf");
    cmd.cwd(&termbox_dir);
    cmd
}

fn run(cmd: &mut Command) {
    println!("running: {}", cmd);
    assert!(cmd.stdout(InheritFd(1))
                .stderr(InheritFd(2))
                .status()
                .unwrap()
                .success());
}
<commit_msg>Fix build issues with latest rust nightly<commit_after>use std::os;
use std::io::Command;
use std::io::process::InheritFd;

fn main() {
    let dst = Path::new(os::getenv("OUT_DIR").unwrap());

    setup();
    configure();
    build();
    install(&dst);
    clean();
    println!("cargo:rustc-flags=-L {} -l termbox:static", dst.join("lib").display());
}

fn setup() {
    let mut cmd = Command::new("git");
    cmd.arg("clone");
    cmd.arg("https://github.com/nsf/termbox");
    cmd.arg(".termbox");
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    cmd.cwd(&cargo_dir);

    run(&mut cmd);
}

fn clean() {
    let mut cmd = Command::new("rm");
    cmd.arg("-rf");
    cmd.arg(".termbox");
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    cmd.cwd(&cargo_dir);
    run(&mut cmd);
}

fn configure() {
    let mut cmd = waf();
    cmd.arg("configure");
    cmd.arg("--prefix=/");

    let target = os::getenv("TARGET").unwrap();
    let mut cflags;
    if target.as_slice().contains("i686") {
        cflags = "-m32"
    } else if target.as_slice().contains("x86_64") {
        cflags = "-m64 -fPIC"
    } else {
        cflags = ""
    }
    println!("waf configure: setting CFLAGS to: `{}`", cflags);
    os::setenv("CFLAGS", cflags);

    run(&mut cmd);
    os::unsetenv("CFLAGS");
}

fn build() {
    let mut cmd = waf();
    cmd.arg("build");
    cmd.arg("--targets=termbox_static");
    run(&mut cmd);
}

fn install(dst: &Path) {
    let mut cmd = waf();
    cmd.arg("install");
    cmd.arg("--targets=termbox_static");
    cmd.arg(format!("--destdir={}", dst.display()));
    run(&mut cmd);
}

fn waf() -> Command {
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    let termbox_dir = cargo_dir.join(".termbox");
    let mut cmd = Command::new("./waf");
    cmd.cwd(&termbox_dir);
    cmd
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(cmd.stdout(InheritFd(1))
                .stderr(InheritFd(2))
                .status()
                .unwrap()
                .success());
}
<|endoftext|>


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

use dom::bindings::callback::{CallbackContainer, ExceptionHandling, CallbackFunction};
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::ErrorEventBinding::ErrorEventMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::Bindings::EventHandlerBinding::OnErrorEventHandlerNonNull;
use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener;
use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
use dom::bindings::codegen::UnionTypes::EventOrString;
use dom::bindings::error::{Error, Fallible, report_pending_exception};
use dom::bindings::global::{GlobalRef, global_root_from_reflector};
use dom::bindings::inheritance::{Castable, EventTargetTypeId};
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflectable, Reflector};
use dom::errorevent::ErrorEvent;
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::eventdispatcher::dispatch_event;
use dom::virtualmethods::VirtualMethods;
use dom::window::Window;
use fnv::FnvHasher;
use js::jsapi::{CompileFunction, JS_GetFunctionObject, RootedValue};
use js::jsapi::{HandleObject, JSContext, RootedFunction};
use js::jsapi::{JSAutoCompartment, JSAutoRequest};
use js::rust::{AutoObjectVectorWrapper, CompileOptionsWrapper};
use libc::{c_char, size_t};
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::hash_state::DefaultState;
use std::default::Default;
use std::ffi::CString;
use std::rc::Rc;
use std::{intrinsics, ptr};
use string_cache::Atom;
use url::Url;
use util::mem::HeapSizeOf;
use util::str::DOMString;

#[derive(PartialEq, Clone, JSTraceable)]
pub enum CommonEventHandler {
    EventHandler(Rc<EventHandlerNonNull>),
    ErrorEventHandler(Rc<OnErrorEventHandlerNonNull>),
}

impl CommonEventHandler {
    fn parent(&self) -> &CallbackFunction {
        match *self {
            CommonEventHandler::EventHandler(ref handler) => &handler.parent,
            CommonEventHandler::ErrorEventHandler(ref handler) => &handler.parent,
        }
    }
}

#[derive(JSTraceable, Copy, Clone, PartialEq, HeapSizeOf)]
pub enum ListenerPhase {
    Capturing,
    Bubbling,
}

impl PartialEq for EventTargetTypeId {
    #[inline]
    fn eq(&self, other: &EventTargetTypeId) -> bool {
        match (*self, *other) {
            (EventTargetTypeId::Node(this_type), EventTargetTypeId::Node(other_type)) => {
                this_type == other_type
            }
            _ => self.eq_slow(other)
        }
    }
}

impl EventTargetTypeId {
    #[allow(unsafe_code)]
    fn eq_slow(&self, other: &EventTargetTypeId) -> bool {
        match (*self, *other) {
            (EventTargetTypeId::Node(this_type), EventTargetTypeId::Node(other_type)) => {
                this_type == other_type
            }
            (EventTargetTypeId::WorkerGlobalScope(this_type),
             EventTargetTypeId::WorkerGlobalScope(other_type)) => {
                this_type == other_type
            }
            (EventTargetTypeId::XMLHttpRequestEventTarget(this_type),
             EventTargetTypeId::XMLHttpRequestEventTarget(other_type)) => {
                this_type == other_type
            }
            (_, _) => {
                unsafe {
                    intrinsics::discriminant_value(self) == intrinsics::discriminant_value(other)
                }
            }
        }
    }
}

#[derive(JSTraceable, Clone, PartialEq)]
pub enum EventListenerType {
    Additive(Rc<EventListener>),
    Inline(CommonEventHandler),
}

impl HeapSizeOf for EventListenerType {
    fn heap_size_of_children(&self) -> usize {
        // FIXME: Rc<T> isn't HeapSizeOf and we can't ignore it due to #6870 and #6871
        0
    }
}

impl EventListenerType {
    // https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm
    pub fn call_or_handle_event<T: Reflectable>(&self,
                                                object: &T,
                                                event: &Event,
                                                exception_handle: ExceptionHandling) {
        // Step 3
        match *self {
            EventListenerType::Additive(ref listener) => {
                let _ = listener.HandleEvent_(object, event, exception_handle);
            },
            EventListenerType::Inline(ref handler) => {
                match *handler {
                    CommonEventHandler::ErrorEventHandler(ref handler) => {
                        if let Some(event) = event.downcast::<ErrorEvent>() {
                            let global = global_root_from_reflector(object);
                            let cx = global.r().get_cx();
                            let error = RootedValue::new(cx, event.Error(cx));
                            let _ = handler.Call_(object,
                                                  EventOrString::eString(event.Message()),
                                                  Some(event.Filename()),
                                                  Some(event.Lineno()),
                                                  Some(event.Colno()),
                                                  Some(error.handle()),
                                                  exception_handle);
                            return;
                        }

                        let _ = handler.Call_(object, EventOrString::eEvent(Root::from_ref(event)),
                                              None, None, None, None, exception_handle);
                    }

                    CommonEventHandler::EventHandler(ref handler) => {
                        let _ = handler.Call_(object, event, exception_handle);
                    }
                }
            },
        }

        // TODO(#8490): step 4 (cancel event based on return value)
    }
}

#[derive(JSTraceable, Clone, PartialEq, HeapSizeOf)]
#[privatize]
pub struct EventListenerEntry {
    phase: ListenerPhase,
    listener: EventListenerType
}

#[dom_struct]
pub struct EventTarget {
    reflector_: Reflector,
    handlers: DOMRefCell<HashMap<Atom, Vec<EventListenerEntry>, DefaultState<FnvHasher>>>,
}

impl EventTarget {
    pub fn new_inherited() -> EventTarget {
        EventTarget {
            reflector_: Reflector::new(),
            handlers: DOMRefCell::new(Default::default()),
        }
    }

    pub fn get_listeners(&self, type_: &Atom) -> Option<Vec<EventListenerType>> {
        self.handlers.borrow().get(type_).map(|listeners| {
            listeners.iter().map(|entry| entry.listener.clone()).collect()
        })
    }

    pub fn get_listeners_for(&self, type_: &Atom, desired_phase: ListenerPhase)
        -> Option<Vec<EventListenerType>> {
        self.handlers.borrow().get(type_).map(|listeners| {
            let filtered = listeners.iter().filter(|entry| entry.phase == desired_phase);
            filtered.map(|entry| entry.listener.clone()).collect()
        })
    }

    pub fn dispatch_event_with_target(&self,
                                  target: &EventTarget,
                                  event: &Event) -> bool {
        dispatch_event(self, Some(target), event)
    }

    pub fn dispatch_event(&self, event: &Event) -> bool {
        dispatch_event(self, None, event)
    }

    pub fn set_inline_event_listener(&self,
                                     ty: Atom,
                                     listener: Option<CommonEventHandler>) {
        let mut handlers = self.handlers.borrow_mut();
        let entries = match handlers.entry(ty) {
            Occupied(entry) => entry.into_mut(),
            Vacant(entry) => entry.insert(vec!()),
        };

        let idx = entries.iter().position(|ref entry| {
            match entry.listener {
                EventListenerType::Inline(_) => true,
                _ => false,
            }
        });

        match idx {
            Some(idx) => {
                match listener {
                    Some(listener) => entries[idx].listener = EventListenerType::Inline(listener),
                    None => {
                        entries.remove(idx);
                    }
                }
            }
            None => {
                if listener.is_some() {
                    entries.push(EventListenerEntry {
                        phase: ListenerPhase::Bubbling,
                        listener: EventListenerType::Inline(listener.unwrap()),
                    });
                }
            }
        }
    }

    pub fn get_inline_event_listener(&self, ty: &Atom) -> Option<CommonEventHandler> {
        let handlers = self.handlers.borrow();
        let entries = handlers.get(ty);
        entries.and_then(|entries| entries.iter().filter_map(|entry| {
            match entry.listener {
                EventListenerType::Inline(ref handler) => Some(handler.clone()),
                _ => None,
            }
        }).next())
    }

    #[allow(unsafe_code)]
    // https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler
    pub fn set_event_handler_uncompiled(&self,
                                    cx: *mut JSContext,
                                    url: Url,
                                    scope: HandleObject,
                                    ty: &str,
                                    source: DOMString) {
        let url = CString::new(url.serialize()).unwrap();
        let name = CString::new(ty).unwrap();
        let lineno = 0; //XXXjdm need to get a real number here

        static mut ARG_NAMES: [*const c_char; 1] = [b"event\0" as *const u8 as *const c_char];
        static mut ERROR_ARG_NAMES: [*const c_char; 5] = [b"event\0" as *const u8 as *const c_char,
                                                          b"source\0" as *const u8 as *const c_char,
                                                          b"lineno\0" as *const u8 as *const c_char,
                                                          b"colno\0" as *const u8 as *const c_char,
                                                          b"error\0" as *const u8 as *const c_char];
        // step 10
        let is_error = ty == "error" && self.is::<Window>();
        let args = unsafe {
            if is_error {
                &ERROR_ARG_NAMES[..]
            } else {
                &ARG_NAMES[..]
            }
        };

        let source: Vec<u16> = source.utf16_units().collect();
        let options = CompileOptionsWrapper::new(cx, url.as_ptr(), lineno);
        let scopechain = AutoObjectVectorWrapper::new(cx);

        let _ar = JSAutoRequest::new(cx);
        let _ac = JSAutoCompartment::new(cx, scope.get());
        let mut handler = RootedFunction::new(cx, ptr::null_mut());
        let rv = unsafe {
            CompileFunction(cx,
                            scopechain.ptr,
                            options.ptr,
                            name.as_ptr(),
                            args.len() as u32,
                            args.as_ptr(),
                            source.as_ptr(),
                            source.len() as size_t,
                            handler.handle_mut())
        };
        if !rv || handler.ptr.is_null() {
            report_pending_exception(cx, self.reflector().get_jsobject().get());
            return;
        }

        let funobj = unsafe { JS_GetFunctionObject(handler.ptr) };
        assert!(!funobj.is_null());
        if is_error {
            self.set_error_event_handler(ty, Some(OnErrorEventHandlerNonNull::new(funobj)));
        } else {
            self.set_event_handler_common(ty, Some(EventHandlerNonNull::new(funobj)));
        }
    }

    pub fn set_event_handler_common<T: CallbackContainer>(
        &self, ty: &str, listener: Option<Rc<T>>)
    {
        let event_listener = listener.map(|listener|
                                          CommonEventHandler::EventHandler(
                                              EventHandlerNonNull::new(listener.callback())));
        self.set_inline_event_listener(Atom::from(ty), event_listener);
    }

    pub fn set_error_event_handler<T: CallbackContainer>(
        &self, ty: &str, listener: Option<Rc<T>>)
    {
        let event_listener = listener.map(|listener|
                                          CommonEventHandler::ErrorEventHandler(
                                              OnErrorEventHandlerNonNull::new(listener.callback())));
        self.set_inline_event_listener(Atom::from(ty), event_listener);
    }

    pub fn get_event_handler_common<T: CallbackContainer>(&self, ty: &str) -> Option<Rc<T>> {
        let listener = self.get_inline_event_listener(&Atom::from(ty));
        listener.map(|listener| CallbackContainer::new(listener.parent().callback()))
    }

    pub fn has_handlers(&self) -> bool {
        !self.handlers.borrow().is_empty()
    }

    /// Implements https://html.spec.whatwg.org/multipage/#fire-a-simple-event
    pub fn fire_simple_event(&self, name: &str, win: GlobalRef) -> Root<Event> {
        self.fire_event(name, EventBubbles::DoesNotBubble,
                        EventCancelable::NotCancelable, win)
    }

    /// Implements more customizable variant of EventTarget::fire_simple_event.
    pub fn fire_event(&self, name: &str,
                      bubbles: EventBubbles,
                      cancelable: EventCancelable,
                      win: GlobalRef) -> Root<Event> {
        let event = Event::new(win, Atom::from(name), bubbles, cancelable);

        event.fire(self);

        event
    }
}

impl EventTargetMethods for EventTarget {
    // https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener
    fn AddEventListener(&self,
                        ty: DOMString,
                        listener: Option<Rc<EventListener>>,
                        capture: bool) {
        if let Some(listener) = listener {
            let mut handlers = self.handlers.borrow_mut();
            let entry = match handlers.entry(Atom::from(&*ty)) {
                Occupied(entry) => entry.into_mut(),
                Vacant(entry) => entry.insert(vec!()),
            };

            let phase = if capture { ListenerPhase::Capturing } else { ListenerPhase::Bubbling };
            let new_entry = EventListenerEntry {
                phase: phase,
                listener: EventListenerType::Additive(listener)
            };
            if !entry.contains(&new_entry) {
                entry.push(new_entry);
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener
    fn RemoveEventListener(&self,
                           ty: DOMString,
                           listener: Option<Rc<EventListener>>,
                           capture: bool) {
        if let Some(ref listener) = listener {
            let mut handlers = self.handlers.borrow_mut();
            let entry = handlers.get_mut(&Atom::from(&*ty));
            for entry in entry {
                let phase = if capture { ListenerPhase::Capturing } else { ListenerPhase::Bubbling };
                let old_entry = EventListenerEntry {
                    phase: phase,
                    listener: EventListenerType::Additive(listener.clone())
                };
                if let Some(position) = entry.iter().position(|e| *e == old_entry) {
                    entry.remove(position);
                }
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent
    fn DispatchEvent(&self, event: &Event) -> Fallible<bool> {
        if event.dispatching() || !event.initialized() {
            return Err(Error::InvalidState);
        }
        event.set_trusted(false);
        Ok(self.dispatch_event(event))
    }
}

impl VirtualMethods for EventTarget {
    fn super_type(&self) -> Option<&VirtualMethods> {
        None
    }
}
<commit_msg>Auto merge of #9218 - KiChjang:doc-fix, r=KiChjang<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::callback::{CallbackContainer, ExceptionHandling, CallbackFunction};
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::ErrorEventBinding::ErrorEventMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::Bindings::EventHandlerBinding::OnErrorEventHandlerNonNull;
use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener;
use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
use dom::bindings::codegen::UnionTypes::EventOrString;
use dom::bindings::error::{Error, Fallible, report_pending_exception};
use dom::bindings::global::{GlobalRef, global_root_from_reflector};
use dom::bindings::inheritance::{Castable, EventTargetTypeId};
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflectable, Reflector};
use dom::errorevent::ErrorEvent;
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::eventdispatcher::dispatch_event;
use dom::virtualmethods::VirtualMethods;
use dom::window::Window;
use fnv::FnvHasher;
use js::jsapi::{CompileFunction, JS_GetFunctionObject, RootedValue};
use js::jsapi::{HandleObject, JSContext, RootedFunction};
use js::jsapi::{JSAutoCompartment, JSAutoRequest};
use js::rust::{AutoObjectVectorWrapper, CompileOptionsWrapper};
use libc::{c_char, size_t};
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::hash_state::DefaultState;
use std::default::Default;
use std::ffi::CString;
use std::rc::Rc;
use std::{intrinsics, ptr};
use string_cache::Atom;
use url::Url;
use util::mem::HeapSizeOf;
use util::str::DOMString;

#[derive(PartialEq, Clone, JSTraceable)]
pub enum CommonEventHandler {
    EventHandler(Rc<EventHandlerNonNull>),
    ErrorEventHandler(Rc<OnErrorEventHandlerNonNull>),
}

impl CommonEventHandler {
    fn parent(&self) -> &CallbackFunction {
        match *self {
            CommonEventHandler::EventHandler(ref handler) => &handler.parent,
            CommonEventHandler::ErrorEventHandler(ref handler) => &handler.parent,
        }
    }
}

#[derive(JSTraceable, Copy, Clone, PartialEq, HeapSizeOf)]
pub enum ListenerPhase {
    Capturing,
    Bubbling,
}

impl PartialEq for EventTargetTypeId {
    #[inline]
    fn eq(&self, other: &EventTargetTypeId) -> bool {
        match (*self, *other) {
            (EventTargetTypeId::Node(this_type), EventTargetTypeId::Node(other_type)) => {
                this_type == other_type
            }
            _ => self.eq_slow(other)
        }
    }
}

impl EventTargetTypeId {
    #[allow(unsafe_code)]
    fn eq_slow(&self, other: &EventTargetTypeId) -> bool {
        match (*self, *other) {
            (EventTargetTypeId::Node(this_type), EventTargetTypeId::Node(other_type)) => {
                this_type == other_type
            }
            (EventTargetTypeId::WorkerGlobalScope(this_type),
             EventTargetTypeId::WorkerGlobalScope(other_type)) => {
                this_type == other_type
            }
            (EventTargetTypeId::XMLHttpRequestEventTarget(this_type),
             EventTargetTypeId::XMLHttpRequestEventTarget(other_type)) => {
                this_type == other_type
            }
            (_, _) => {
                unsafe {
                    intrinsics::discriminant_value(self) == intrinsics::discriminant_value(other)
                }
            }
        }
    }
}

#[derive(JSTraceable, Clone, PartialEq)]
pub enum EventListenerType {
    Additive(Rc<EventListener>),
    Inline(CommonEventHandler),
}

impl HeapSizeOf for EventListenerType {
    fn heap_size_of_children(&self) -> usize {
        // FIXME: Rc<T> isn't HeapSizeOf and we can't ignore it due to #6870 and #6871
        0
    }
}

impl EventListenerType {
    // https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm
    pub fn call_or_handle_event<T: Reflectable>(&self,
                                                object: &T,
                                                event: &Event,
                                                exception_handle: ExceptionHandling) {
        // Step 3
        match *self {
            EventListenerType::Additive(ref listener) => {
                let _ = listener.HandleEvent_(object, event, exception_handle);
            },
            EventListenerType::Inline(ref handler) => {
                match *handler {
                    CommonEventHandler::ErrorEventHandler(ref handler) => {
                        if let Some(event) = event.downcast::<ErrorEvent>() {
                            let global = global_root_from_reflector(object);
                            let cx = global.r().get_cx();
                            let error = RootedValue::new(cx, event.Error(cx));
                            let _ = handler.Call_(object,
                                                  EventOrString::eString(event.Message()),
                                                  Some(event.Filename()),
                                                  Some(event.Lineno()),
                                                  Some(event.Colno()),
                                                  Some(error.handle()),
                                                  exception_handle);
                            return;
                        }

                        let _ = handler.Call_(object, EventOrString::eEvent(Root::from_ref(event)),
                                              None, None, None, None, exception_handle);
                    }

                    CommonEventHandler::EventHandler(ref handler) => {
                        let _ = handler.Call_(object, event, exception_handle);
                    }
                }
            },
        }

        // TODO(#8490): step 4 (cancel event based on return value)
    }
}

#[derive(JSTraceable, Clone, PartialEq, HeapSizeOf)]
#[privatize]
pub struct EventListenerEntry {
    phase: ListenerPhase,
    listener: EventListenerType
}

#[dom_struct]
pub struct EventTarget {
    reflector_: Reflector,
    handlers: DOMRefCell<HashMap<Atom, Vec<EventListenerEntry>, DefaultState<FnvHasher>>>,
}

impl EventTarget {
    pub fn new_inherited() -> EventTarget {
        EventTarget {
            reflector_: Reflector::new(),
            handlers: DOMRefCell::new(Default::default()),
        }
    }

    pub fn get_listeners(&self, type_: &Atom) -> Option<Vec<EventListenerType>> {
        self.handlers.borrow().get(type_).map(|listeners| {
            listeners.iter().map(|entry| entry.listener.clone()).collect()
        })
    }

    pub fn get_listeners_for(&self, type_: &Atom, desired_phase: ListenerPhase)
        -> Option<Vec<EventListenerType>> {
        self.handlers.borrow().get(type_).map(|listeners| {
            let filtered = listeners.iter().filter(|entry| entry.phase == desired_phase);
            filtered.map(|entry| entry.listener.clone()).collect()
        })
    }

    pub fn dispatch_event_with_target(&self,
                                  target: &EventTarget,
                                  event: &Event) -> bool {
        dispatch_event(self, Some(target), event)
    }

    pub fn dispatch_event(&self, event: &Event) -> bool {
        dispatch_event(self, None, event)
    }

    pub fn set_inline_event_listener(&self,
                                     ty: Atom,
                                     listener: Option<CommonEventHandler>) {
        let mut handlers = self.handlers.borrow_mut();
        let entries = match handlers.entry(ty) {
            Occupied(entry) => entry.into_mut(),
            Vacant(entry) => entry.insert(vec!()),
        };

        let idx = entries.iter().position(|ref entry| {
            match entry.listener {
                EventListenerType::Inline(_) => true,
                _ => false,
            }
        });

        match idx {
            Some(idx) => {
                match listener {
                    Some(listener) => entries[idx].listener = EventListenerType::Inline(listener),
                    None => {
                        entries.remove(idx);
                    }
                }
            }
            None => {
                if listener.is_some() {
                    entries.push(EventListenerEntry {
                        phase: ListenerPhase::Bubbling,
                        listener: EventListenerType::Inline(listener.unwrap()),
                    });
                }
            }
        }
    }

    pub fn get_inline_event_listener(&self, ty: &Atom) -> Option<CommonEventHandler> {
        let handlers = self.handlers.borrow();
        let entries = handlers.get(ty);
        entries.and_then(|entries| entries.iter().filter_map(|entry| {
            match entry.listener {
                EventListenerType::Inline(ref handler) => Some(handler.clone()),
                _ => None,
            }
        }).next())
    }

    #[allow(unsafe_code)]
    // https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler
    pub fn set_event_handler_uncompiled(&self,
                                    cx: *mut JSContext,
                                    url: Url,
                                    scope: HandleObject,
                                    ty: &str,
                                    source: DOMString) {
        let url = CString::new(url.serialize()).unwrap();
        let name = CString::new(ty).unwrap();
        let lineno = 0; //XXXjdm need to get a real number here

        static mut ARG_NAMES: [*const c_char; 1] = [b"event\0" as *const u8 as *const c_char];
        static mut ERROR_ARG_NAMES: [*const c_char; 5] = [b"event\0" as *const u8 as *const c_char,
                                                          b"source\0" as *const u8 as *const c_char,
                                                          b"lineno\0" as *const u8 as *const c_char,
                                                          b"colno\0" as *const u8 as *const c_char,
                                                          b"error\0" as *const u8 as *const c_char];
        // step 10
        let is_error = ty == "error" && self.is::<Window>();
        let args = unsafe {
            if is_error {
                &ERROR_ARG_NAMES[..]
            } else {
                &ARG_NAMES[..]
            }
        };

        let source: Vec<u16> = source.utf16_units().collect();
        let options = CompileOptionsWrapper::new(cx, url.as_ptr(), lineno);
        let scopechain = AutoObjectVectorWrapper::new(cx);

        let _ar = JSAutoRequest::new(cx);
        let _ac = JSAutoCompartment::new(cx, scope.get());
        let mut handler = RootedFunction::new(cx, ptr::null_mut());
        let rv = unsafe {
            CompileFunction(cx,
                            scopechain.ptr,
                            options.ptr,
                            name.as_ptr(),
                            args.len() as u32,
                            args.as_ptr(),
                            source.as_ptr(),
                            source.len() as size_t,
                            handler.handle_mut())
        };
        if !rv || handler.ptr.is_null() {
            report_pending_exception(cx, self.reflector().get_jsobject().get());
            return;
        }

        let funobj = unsafe { JS_GetFunctionObject(handler.ptr) };
        assert!(!funobj.is_null());
        if is_error {
            self.set_error_event_handler(ty, Some(OnErrorEventHandlerNonNull::new(funobj)));
        } else {
            self.set_event_handler_common(ty, Some(EventHandlerNonNull::new(funobj)));
        }
    }

    pub fn set_event_handler_common<T: CallbackContainer>(
        &self, ty: &str, listener: Option<Rc<T>>)
    {
        let event_listener = listener.map(|listener|
                                          CommonEventHandler::EventHandler(
                                              EventHandlerNonNull::new(listener.callback())));
        self.set_inline_event_listener(Atom::from(ty), event_listener);
    }

    pub fn set_error_event_handler<T: CallbackContainer>(
        &self, ty: &str, listener: Option<Rc<T>>)
    {
        let event_listener = listener.map(|listener|
                                          CommonEventHandler::ErrorEventHandler(
                                              OnErrorEventHandlerNonNull::new(listener.callback())));
        self.set_inline_event_listener(Atom::from(ty), event_listener);
    }

    pub fn get_event_handler_common<T: CallbackContainer>(&self, ty: &str) -> Option<Rc<T>> {
        let listener = self.get_inline_event_listener(&Atom::from(ty));
        listener.map(|listener| CallbackContainer::new(listener.parent().callback()))
    }

    pub fn has_handlers(&self) -> bool {
        !self.handlers.borrow().is_empty()
    }

    // https://html.spec.whatwg.org/multipage/#fire-a-simple-event
    pub fn fire_simple_event(&self, name: &str, win: GlobalRef) -> Root<Event> {
        self.fire_event(name, EventBubbles::DoesNotBubble,
                        EventCancelable::NotCancelable, win)
    }

    // https://dom.spec.whatwg.org/#concept-event-fire
    pub fn fire_event(&self, name: &str,
                      bubbles: EventBubbles,
                      cancelable: EventCancelable,
                      win: GlobalRef) -> Root<Event> {
        let event = Event::new(win, Atom::from(name), bubbles, cancelable);

        event.fire(self);

        event
    }
}

impl EventTargetMethods for EventTarget {
    // https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener
    fn AddEventListener(&self,
                        ty: DOMString,
                        listener: Option<Rc<EventListener>>,
                        capture: bool) {
        if let Some(listener) = listener {
            let mut handlers = self.handlers.borrow_mut();
            let entry = match handlers.entry(Atom::from(&*ty)) {
                Occupied(entry) => entry.into_mut(),
                Vacant(entry) => entry.insert(vec!()),
            };

            let phase = if capture { ListenerPhase::Capturing } else { ListenerPhase::Bubbling };
            let new_entry = EventListenerEntry {
                phase: phase,
                listener: EventListenerType::Additive(listener)
            };
            if !entry.contains(&new_entry) {
                entry.push(new_entry);
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener
    fn RemoveEventListener(&self,
                           ty: DOMString,
                           listener: Option<Rc<EventListener>>,
                           capture: bool) {
        if let Some(ref listener) = listener {
            let mut handlers = self.handlers.borrow_mut();
            let entry = handlers.get_mut(&Atom::from(&*ty));
            for entry in entry {
                let phase = if capture { ListenerPhase::Capturing } else { ListenerPhase::Bubbling };
                let old_entry = EventListenerEntry {
                    phase: phase,
                    listener: EventListenerType::Additive(listener.clone())
                };
                if let Some(position) = entry.iter().position(|e| *e == old_entry) {
                    entry.remove(position);
                }
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent
    fn DispatchEvent(&self, event: &Event) -> Fallible<bool> {
        if event.dispatching() || !event.initialized() {
            return Err(Error::InvalidState);
        }
        event.set_trusted(false);
        Ok(self.dispatch_event(event))
    }
}

impl VirtualMethods for EventTarget {
    fn super_type(&self) -> Option<&VirtualMethods> {
        None
    }
}
<|endoftext|>


<commit_before>extern crate std;

pub use self::state::{ EntitiesState, Data, EntityID, ComponentID, Number, String, Boolean, List, Entity, FromProperty, Empty };
pub use self::state::{ ComponentType, NativeComponentType, EntityComponentType };
pub use self::state::{ StateError };

pub mod loader;
mod state;

pub trait EntitiesHelper {
    /**
     * Returns the type of the component
     */
    fn get_type(&self, id: &ComponentID)
        -> Result<ComponentType, StateError>;

    /**
     * Returns the list of all the components in the state
     */
    fn get_components_list(&self)
        -> Vec<ComponentID>;

    /**
     * Returns the owner of the component
     */
    fn get_owner(&self, id: &ComponentID)
        -> Result<EntityID, StateError>;

    /**
     * Returns an element of a component
     */
    fn get<'a>(&'a self, id: &ComponentID, field: &str)
        -> Result<&'a Data, StateError>;

    fn get_and_resolve(&self, id: &ComponentID, field: &str)
        -> Result<Data, StateError>
    {
        let propname = match try!(self.get(id, field)) {
            &FromProperty(ref p) => p,
            a => return Ok(a.clone())
        };

        let owner = try!(self.get_owner(id));

        let value = self
            .get_components_list().move_iter()
            .filter(|c| self.get_owner(c).unwrap() == owner)
            .filter(|c| match self.get_type(c) { Ok(NativeComponentType(t)) => t.as_slice() == "property", _ => false })
            .filter(|c| match self.get(c, "property") { Ok(&String(ref n)) => n == propname, _ => false })
            .max_by(|c| match self.get(c, "priority") { Ok(&::entities::Number(ref n)) => (((*n) * 1000f64) as int), _ => 1000 })
            .and_then(|c| match self.get(&c, "value") { Ok(&FromProperty(_)) => None, Ok(n) => Some(n.clone()), _ => None });

        match value {
            Some(v) => Ok(v),
            None => Ok(Empty)
        }
    }

    fn get_as_number(&self, id: &ComponentID, field: &str)
        -> Option<f64>
    {
        match self.get_and_resolve(id, field) {
            Ok(Number(n)) => Some(n),
            _ => None
        }
    }

    fn get_as_string(&self, id: &ComponentID, field: &str)
        -> Option<String>
    {
        match self.get_and_resolve(id, field) {
            Ok(String(s)) => Some(s),
            _ => None
        }
    }

    fn get_as_boolean(&self, id: &ComponentID, field: &str)
        -> Option<bool>
    {
        match self.get_and_resolve(id, field) {
            Ok(Boolean(b)) => Some(b),
            _ => None
        }
    }

    fn get_as_entity(&self, id: &ComponentID, field: &str)
        -> Option<EntityID>
    {
        match self.get_and_resolve(id, field) {
            Ok(Entity(e)) => Some(e),
            _ => None
        }
    }
}
<commit_msg>Minor reorganization in EntitiesHelper<commit_after>extern crate std;

pub use self::state::{ EntitiesState, Data, EntityID, ComponentID, Number, String, Boolean, List, Entity, FromProperty, Empty };
pub use self::state::{ ComponentType, NativeComponentType, EntityComponentType };
pub use self::state::{ StateError };

pub mod loader;
mod state;

pub trait EntitiesHelper {
    /**
     * Returns the type of the component
     */
    fn get_type(&self, id: &ComponentID)
        -> Result<ComponentType, StateError>;

    /**
     * Returns the list of all the components in the state
     */
    fn get_components_list(&self)
        -> Vec<ComponentID>;

    /**
     * Returns the owner of the component
     */
    fn get_owner(&self, id: &ComponentID)
        -> Result<EntityID, StateError>;

    /**
     * Returns an element of a component
     */
    fn get<'a>(&'a self, id: &ComponentID, field: &str)
        -> Result<&'a Data, StateError>;

    fn get_property_value(&self, id: &EntityID, propname: &str)
        -> Result<Data, StateError>
    {
        let value = self
            .get_components_list().move_iter()
            .filter(|c| &self.get_owner(c).unwrap() == id)
            .filter(|c| match self.get_type(c) { Ok(NativeComponentType(t)) => t.as_slice() == "property", _ => false })
            .filter(|c| match self.get(c, "property") { Ok(&String(ref n)) => n.as_slice() == propname, _ => false })
            .max_by(|c| match self.get(c, "priority") { Ok(&::entities::Number(ref n)) => (((*n) * 1000f64) as int), _ => 1000 })
            .and_then(|c| match self.get(&c, "value") { Ok(&FromProperty(_)) => None, Ok(n) => Some(n.clone()), _ => None });

        match value {
            Some(v) => Ok(v),
            None => Ok(Empty)
        }
    }

    fn get_and_resolve(&self, id: &ComponentID, field: &str)
        -> Result<Data, StateError>
    {
        let propname = match try!(self.get(id, field)) {
            &FromProperty(ref p) => p,
            a => return Ok(a.clone())
        };

        let owner = try!(self.get_owner(id));

        self.get_property_value(&owner, propname.as_slice())
    }

    fn get_as_number(&self, id: &ComponentID, field: &str)
        -> Option<f64>
    {
        match self.get_and_resolve(id, field) {
            Ok(Number(n)) => Some(n),
            _ => None
        }
    }

    fn get_as_string(&self, id: &ComponentID, field: &str)
        -> Option<String>
    {
        match self.get_and_resolve(id, field) {
            Ok(String(s)) => Some(s),
            _ => None
        }
    }

    fn get_as_boolean(&self, id: &ComponentID, field: &str)
        -> Option<bool>
    {
        match self.get_and_resolve(id, field) {
            Ok(Boolean(b)) => Some(b),
            _ => None
        }
    }

    fn get_as_entity(&self, id: &ComponentID, field: &str)
        -> Option<EntityID>
    {
        match self.get_and_resolve(id, field) {
            Ok(Entity(e)) => Some(e),
            _ => None
        }
    }
}
<|endoftext|>


<commit_before>//! CHIP-8 emulator backend.
//! You can develop your own frontends for it.
//!
//! The reference frontend is
//! [crusty-chip-sfml](https://github.com/crumblingstatue/crusty-chip-sfml).

use std::slice::bytes::copy_memory;

static START_ADDR: u16 = 0x200;
static MEM_SIZE: uint = 4096;
pub static DISPLAY_WIDTH: uint = 64;
pub static DISPLAY_HEIGHT: uint = 32;

static fontset: [[u8, .. 5], .. 0x10] = [
    [0xF0, 0x90, 0x90, 0x90, 0xF0], // 0
    [0x20, 0x60, 0x20, 0x20, 0x70], // 1
    [0xF0, 0x10, 0xF0, 0x80, 0xF0], // 2
    [0xF0, 0x10, 0xF0, 0x10, 0xF0], // 3
    [0x90, 0x90, 0xF0, 0x10, 0x10], // 4
    [0xF0, 0x80, 0xF0, 0x10, 0xF0], // 5
    [0xF0, 0x80, 0xF0, 0x90, 0xF0], // 6
    [0xF0, 0x10, 0x20, 0x40, 0x40], // 7
    [0xF0, 0x90, 0xF0, 0x90, 0xF0], // 8
    [0xF0, 0x90, 0xF0, 0x10, 0xF0], // 9
    [0xF0, 0x90, 0xF0, 0x90, 0x90], // A
    [0xE0, 0x90, 0xE0, 0x90, 0xE0], // B
    [0xF0, 0x80, 0x80, 0x80, 0xF0], // C
    [0xE0, 0x90, 0x90, 0x90, 0xE0], // D
    [0xF0, 0x80, 0xF0, 0x80, 0xF0], // E
    [0xF0, 0x80, 0xF0, 0x80, 0x80], // F
];

type DrawCallback<'a> = |pixels: &[u8]|: 'a;

/// CHIP-8 virtual machine
pub struct Chip8<'a> {
    ram: [u8, .. MEM_SIZE],
    v: [u8, .. 16],
    i: u16,
    delay_timer: u8,
    sound_timer: u8,
    pc: u16,
    sp: u8,
    stack: [u16, .. 16],
    display: [u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
    draw_callback: DrawCallback<'a>
}

impl <'a> Chip8 <'a> {

    /// Create a new Chip8 VM
    ///
    /// ## Arguments ##
    /// * draw_callback - Callback used when drawing
    pub fn new(draw_callback: DrawCallback<'a>) -> Chip8<'a> {
        Chip8 {
            ram: [0u8, .. MEM_SIZE],
            v: [0u8, .. 16],
            i: 0u16,
            delay_timer: 0u8,
            sound_timer: 0u8,
            pc: START_ADDR,
            sp: 0,
            stack: [0u16, .. 16],
            display: [0u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
            draw_callback: draw_callback
        }
    }

    /// Load a ROM
    ///
    /// ## Arguments ##
    /// * rom - ROM to load
    pub fn load_rom(&mut self, rom: &[u8]) {
        let len = self.ram.len();
        copy_memory(self.ram.mut_slice(START_ADDR as uint, len), rom);
    }

    /// Do an emulation cycle.
    pub fn do_cycle(&mut self) {
        let ins = self.get_ins();
        self.pc += 2;

        match ins & 0xF000 {
            0x1000 => self.jump_addr(ins & 0x0FFF),
            0x3000 => self.skip_next_vx_eq(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x5000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_eq_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x5XXX instruction: {:x}", ins)
            },
            0x6000 => self.set_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x7000 => self.add_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x8000 => match ins & 0x000F {
                0x0000 => self.set_vx_to_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0004 => self.add_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0005 => self.sub_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x8XXX instruction: {:x}", ins)
            },
            0x9000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_ne_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x9XXX instruction: {:x}", ins)
            },
            0xA000 => self.set_i(ins & 0x0FFF),
            0xC000 => self.set_vx_rand_and(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0xD000 => self.display_sprite(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint, ((ins & 0x000F)) as uint),
            0xF000 => match ins & 0x00FF {
                0x0055 => self.copy_v0_through_vx_to_mem(((ins & 0x0F00) >> 8) as uint),
                0x0065 => self.read_v0_through_vx_from_mem(((ins & 0x0F00) >> 8) as uint),
                0x001E => self.add_vx_to_i(((ins & 0x0F00) >> 8) as uint),
                _ => fail!("Unknown 0xFXXX instruction: {:x}", ins)
            },
            _ => fail!("Unknown instruction: {:x}", ins)
        }
    }

    fn get_ins(&self) -> u16 {
        let b1 = self.ram[self.pc as uint];
        let b2 = self.ram[(self.pc + 1) as uint];
        b1 as u16 << 8 | b2 as u16
    }

    fn set_i(&mut self, to: u16) {
        self.i = to;
    }

    fn set_vx_rand_and(&mut self, x: uint, to: u8) {
        use std::rand::{task_rng, Rng};
        let mut rgen = task_rng();
        self.v[x] = rgen.gen::<u8>() & to;
    }

    fn skip_next_vx_eq(&mut self, x: uint, to: u8) {
        if self.v[x] == to {
            self.pc += 2;
        }
    }

    fn display_sprite(&mut self, vx: uint, vy: uint, n: uint) {
        let x_off = self.v[vx] as uint;
        let y_off = self.v[vy] as uint * DISPLAY_WIDTH;
        let offset = x_off + y_off;

        for mut y in range(0u, n) {
            if y >= DISPLAY_HEIGHT {
                y = y - DISPLAY_HEIGHT;
            }
            let b = self.ram[self.i as uint + y];
            for mut x in range(0u, 8) {
                if x >= DISPLAY_WIDTH {
                    x = x - DISPLAY_WIDTH;
                }
                let idx = offset + (y * DISPLAY_WIDTH) + x;
                if idx < DISPLAY_WIDTH * DISPLAY_HEIGHT {
                    self.display[idx] ^= b & (0b10000000 >> x);
                } else {
                    println!("Warning: Out of bounds VRAM write: {}", idx);
                }
            }
        }

        (self.draw_callback)(self.display);
    }

    fn add_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] += byte;
    }

    fn set_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] = byte;
    }

    fn jump_addr(&mut self, addr: u16) {
        self.pc = addr;
    }

    fn set_vx_to_vy(&mut self, x: uint, y: uint) {
        self.v[x] = self.v[y];
    }

    fn add_vx_vy(&mut self, x: uint, y: uint) {
        let result = (self.v[x] + self.v[y]) as u16;
        self.v[0xF] = (result > 255) as u8;
        self.v[x] = result as u8;
    }

    fn sub_vx_vy(&mut self, x: uint, y: uint) {
        self.v[0xF] = (self.v[x] > self.v[y]) as u8;
        self.v[x] -= self.v[y];
    }

    fn add_vx_to_i(&mut self, x: uint) {
        self.i += x as u16;
    }

    fn copy_v0_through_vx_to_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.ram.mut_slice(self.i as uint, x), self.v.slice(0, x));
    }

    fn read_v0_through_vx_from_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.v.mut_slice(0, x), self.ram.slice(self.i as uint, x));
    }

    fn skip_next_vx_eq_vy(&mut self, x: uint, y: uint) {
        if self.v[x] == self.v[y] {
            self.pc += 2;
        }
    }

    fn skip_next_vx_ne_vy(&mut self, x: uint, y: uint) {
        if self.v[x] != self.v[y] {
            self.pc += 2;
        }
    }
}
<commit_msg>Fix (read/copy)_v0_through_vx_* functions<commit_after>//! CHIP-8 emulator backend.
//! You can develop your own frontends for it.
//!
//! The reference frontend is
//! [crusty-chip-sfml](https://github.com/crumblingstatue/crusty-chip-sfml).

use std::slice::bytes::copy_memory;

static START_ADDR: u16 = 0x200;
static MEM_SIZE: uint = 4096;
pub static DISPLAY_WIDTH: uint = 64;
pub static DISPLAY_HEIGHT: uint = 32;

static fontset: [[u8, .. 5], .. 0x10] = [
    [0xF0, 0x90, 0x90, 0x90, 0xF0], // 0
    [0x20, 0x60, 0x20, 0x20, 0x70], // 1
    [0xF0, 0x10, 0xF0, 0x80, 0xF0], // 2
    [0xF0, 0x10, 0xF0, 0x10, 0xF0], // 3
    [0x90, 0x90, 0xF0, 0x10, 0x10], // 4
    [0xF0, 0x80, 0xF0, 0x10, 0xF0], // 5
    [0xF0, 0x80, 0xF0, 0x90, 0xF0], // 6
    [0xF0, 0x10, 0x20, 0x40, 0x40], // 7
    [0xF0, 0x90, 0xF0, 0x90, 0xF0], // 8
    [0xF0, 0x90, 0xF0, 0x10, 0xF0], // 9
    [0xF0, 0x90, 0xF0, 0x90, 0x90], // A
    [0xE0, 0x90, 0xE0, 0x90, 0xE0], // B
    [0xF0, 0x80, 0x80, 0x80, 0xF0], // C
    [0xE0, 0x90, 0x90, 0x90, 0xE0], // D
    [0xF0, 0x80, 0xF0, 0x80, 0xF0], // E
    [0xF0, 0x80, 0xF0, 0x80, 0x80], // F
];

type DrawCallback<'a> = |pixels: &[u8]|: 'a;

/// CHIP-8 virtual machine
pub struct Chip8<'a> {
    ram: [u8, .. MEM_SIZE],
    v: [u8, .. 16],
    i: u16,
    delay_timer: u8,
    sound_timer: u8,
    pc: u16,
    sp: u8,
    stack: [u16, .. 16],
    display: [u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
    draw_callback: DrawCallback<'a>
}

impl <'a> Chip8 <'a> {

    /// Create a new Chip8 VM
    ///
    /// ## Arguments ##
    /// * draw_callback - Callback used when drawing
    pub fn new(draw_callback: DrawCallback<'a>) -> Chip8<'a> {
        Chip8 {
            ram: [0u8, .. MEM_SIZE],
            v: [0u8, .. 16],
            i: 0u16,
            delay_timer: 0u8,
            sound_timer: 0u8,
            pc: START_ADDR,
            sp: 0,
            stack: [0u16, .. 16],
            display: [0u8, .. DISPLAY_WIDTH * DISPLAY_HEIGHT],
            draw_callback: draw_callback
        }
    }

    /// Load a ROM
    ///
    /// ## Arguments ##
    /// * rom - ROM to load
    pub fn load_rom(&mut self, rom: &[u8]) {
        let len = self.ram.len();
        copy_memory(self.ram.mut_slice(START_ADDR as uint, len), rom);
    }

    /// Do an emulation cycle.
    pub fn do_cycle(&mut self) {
        let ins = self.get_ins();
        self.pc += 2;

        match ins & 0xF000 {
            0x1000 => self.jump_addr(ins & 0x0FFF),
            0x3000 => self.skip_next_vx_eq(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x5000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_eq_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x5XXX instruction: {:x}", ins)
            },
            0x6000 => self.set_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x7000 => self.add_vx_byte(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0x8000 => match ins & 0x000F {
                0x0000 => self.set_vx_to_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0004 => self.add_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                0x0005 => self.sub_vx_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x8XXX instruction: {:x}", ins)
            },
            0x9000 => match ins & 0x000F {
                0x0000 => self.skip_next_vx_ne_vy(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint),
                _ => fail!("Unknown 0x9XXX instruction: {:x}", ins)
            },
            0xA000 => self.set_i(ins & 0x0FFF),
            0xC000 => self.set_vx_rand_and(((ins & 0x0F00) >> 8) as uint, (ins & 0x00FF) as u8),
            0xD000 => self.display_sprite(((ins & 0x0F00) >> 8) as uint, ((ins & 0x00F0) >> 4) as uint, ((ins & 0x000F)) as uint),
            0xF000 => match ins & 0x00FF {
                0x0055 => self.copy_v0_through_vx_to_mem(((ins & 0x0F00) >> 8) as uint),
                0x0065 => self.read_v0_through_vx_from_mem(((ins & 0x0F00) >> 8) as uint),
                0x001E => self.add_vx_to_i(((ins & 0x0F00) >> 8) as uint),
                _ => fail!("Unknown 0xFXXX instruction: {:x}", ins)
            },
            _ => fail!("Unknown instruction: {:x}", ins)
        }
    }

    fn get_ins(&self) -> u16 {
        let b1 = self.ram[self.pc as uint];
        let b2 = self.ram[(self.pc + 1) as uint];
        b1 as u16 << 8 | b2 as u16
    }

    fn set_i(&mut self, to: u16) {
        self.i = to;
    }

    fn set_vx_rand_and(&mut self, x: uint, to: u8) {
        use std::rand::{task_rng, Rng};
        let mut rgen = task_rng();
        self.v[x] = rgen.gen::<u8>() & to;
    }

    fn skip_next_vx_eq(&mut self, x: uint, to: u8) {
        if self.v[x] == to {
            self.pc += 2;
        }
    }

    fn display_sprite(&mut self, vx: uint, vy: uint, n: uint) {
        let x_off = self.v[vx] as uint;
        let y_off = self.v[vy] as uint * DISPLAY_WIDTH;
        let offset = x_off + y_off;

        for mut y in range(0u, n) {
            if y >= DISPLAY_HEIGHT {
                y = y - DISPLAY_HEIGHT;
            }
            let b = self.ram[self.i as uint + y];
            for mut x in range(0u, 8) {
                if x >= DISPLAY_WIDTH {
                    x = x - DISPLAY_WIDTH;
                }
                let idx = offset + (y * DISPLAY_WIDTH) + x;
                if idx < DISPLAY_WIDTH * DISPLAY_HEIGHT {
                    self.display[idx] ^= b & (0b10000000 >> x);
                } else {
                    println!("Warning: Out of bounds VRAM write: {}", idx);
                }
            }
        }

        (self.draw_callback)(self.display);
    }

    fn add_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] += byte;
    }

    fn set_vx_byte(&mut self, x: uint, byte: u8) {
        self.v[x] = byte;
    }

    fn jump_addr(&mut self, addr: u16) {
        self.pc = addr;
    }

    fn set_vx_to_vy(&mut self, x: uint, y: uint) {
        self.v[x] = self.v[y];
    }

    fn add_vx_vy(&mut self, x: uint, y: uint) {
        let result = (self.v[x] + self.v[y]) as u16;
        self.v[0xF] = (result > 255) as u8;
        self.v[x] = result as u8;
    }

    fn sub_vx_vy(&mut self, x: uint, y: uint) {
        self.v[0xF] = (self.v[x] > self.v[y]) as u8;
        self.v[x] -= self.v[y];
    }

    fn add_vx_to_i(&mut self, x: uint) {
        self.i += x as u16;
    }

    fn copy_v0_through_vx_to_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.ram.mut_slice(self.i as uint, self.i as uint + x), self.v.slice(0, x));
    }

    fn read_v0_through_vx_from_mem(&mut self, x: uint) {
        if x == 0 {
            return;
        }
        copy_memory(self.v.mut_slice(0, x), self.ram.slice(self.i as uint, self.i as uint + x));
    }

    fn skip_next_vx_eq_vy(&mut self, x: uint, y: uint) {
        if self.v[x] == self.v[y] {
            self.pc += 2;
        }
    }

    fn skip_next_vx_ne_vy(&mut self, x: uint, y: uint) {
        if self.v[x] != self.v[y] {
            self.pc += 2;
        }
    }
}
<|endoftext|>


<commit_before>//! Name resolution for expressions.
use std::sync::Arc;

use hir_expand::name::Name;
use la_arena::{Arena, Idx};
use rustc_hash::FxHashMap;

use crate::{
    body::Body,
    db::DefDatabase,
    expr::{Expr, ExprId, Pat, PatId, Statement},
    DefWithBodyId,
};

pub type ScopeId = Idx<ScopeData>;

#[derive(Debug, PartialEq, Eq)]
pub struct ExprScopes {
    scopes: Arena<ScopeData>,
    scope_by_expr: FxHashMap<ExprId, ScopeId>,
}

#[derive(Debug, PartialEq, Eq)]
pub struct ScopeEntry {
    name: Name,
    pat: PatId,
}

impl ScopeEntry {
    pub fn name(&self) -> &Name {
        &self.name
    }

    pub fn pat(&self) -> PatId {
        self.pat
    }
}

#[derive(Debug, PartialEq, Eq)]
pub struct ScopeData {
    parent: Option<ScopeId>,
    entries: Vec<ScopeEntry>,
}

impl ExprScopes {
    pub(crate) fn expr_scopes_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<ExprScopes> {
        let body = db.body(def);
        Arc::new(ExprScopes::new(&*body))
    }

    fn new(body: &Body) -> ExprScopes {
        let mut scopes =
            ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() };
        let root = scopes.root_scope();
        scopes.add_params_bindings(body, root, &body.params);
        compute_expr_scopes(body.body_expr, body, &mut scopes, root);
        scopes
    }

    pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
        &self.scopes[scope].entries
    }

    pub fn scope_chain(&self, scope: Option<ScopeId>) -> impl Iterator<Item = ScopeId> + '_ {
        std::iter::successors(scope, move |&scope| self.scopes[scope].parent)
    }

    pub fn resolve_name_in_scope(&self, scope: ScopeId, name: &Name) -> Option<&ScopeEntry> {
        self.scope_chain(Some(scope))
            .find_map(|scope| self.entries(scope).iter().find(|it| it.name == *name))
    }

    pub fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
        self.scope_by_expr.get(&expr).copied()
    }

    pub fn scope_by_expr(&self) -> &FxHashMap<ExprId, ScopeId> {
        &self.scope_by_expr
    }

    fn root_scope(&mut self) -> ScopeId {
        self.scopes.alloc(ScopeData { parent: None, entries: vec![] })
    }

    fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
        self.scopes.alloc(ScopeData { parent: Some(parent), entries: vec![] })
    }

    fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
        let pattern = &body[pat];
        if let Pat::Bind { name, .. } = pattern {
            let entry = ScopeEntry { name: name.clone(), pat };
            self.scopes[scope].entries.push(entry);
        }

        pattern.walk_child_pats(|pat| self.add_bindings(body, scope, pat));
    }

    fn add_params_bindings(&mut self, body: &Body, scope: ScopeId, params: &[PatId]) {
        params.iter().for_each(|pat| self.add_bindings(body, scope, *pat));
    }

    fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
        self.scope_by_expr.insert(node, scope);
    }
}

fn compute_block_scopes(
    statements: &[Statement],
    tail: Option<ExprId>,
    body: &Body,
    scopes: &mut ExprScopes,
    mut scope: ScopeId,
) {
    for stmt in statements {
        match stmt {
            Statement::Let { pat, initializer, .. } => {
                if let Some(expr) = initializer {
                    scopes.set_scope(*expr, scope);
                    compute_expr_scopes(*expr, body, scopes, scope);
                }
                scope = scopes.new_scope(scope);
                scopes.add_bindings(body, scope, *pat);
            }
            Statement::Expr(expr) => {
                scopes.set_scope(*expr, scope);
                compute_expr_scopes(*expr, body, scopes, scope);
            }
        }
    }
    if let Some(expr) = tail {
        compute_expr_scopes(expr, body, scopes, scope);
    }
}

fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) {
    scopes.set_scope(expr, scope);
    match &body[expr] {
        Expr::Block { statements, tail, .. } => {
            compute_block_scopes(&statements, *tail, body, scopes, scope);
        }
        Expr::For { iterable, pat, body: body_expr, .. } => {
            compute_expr_scopes(*iterable, body, scopes, scope);
            let scope = scopes.new_scope(scope);
            scopes.add_bindings(body, scope, *pat);
            compute_expr_scopes(*body_expr, body, scopes, scope);
        }
        Expr::Lambda { args, body: body_expr, .. } => {
            let scope = scopes.new_scope(scope);
            scopes.add_params_bindings(body, scope, &args);
            compute_expr_scopes(*body_expr, body, scopes, scope);
        }
        Expr::Match { expr, arms } => {
            compute_expr_scopes(*expr, body, scopes, scope);
            for arm in arms {
                let scope = scopes.new_scope(scope);
                scopes.add_bindings(body, scope, arm.pat);
                if let Some(guard) = arm.guard {
                    scopes.set_scope(guard, scope);
                    compute_expr_scopes(guard, body, scopes, scope);
                }
                scopes.set_scope(arm.expr, scope);
                compute_expr_scopes(arm.expr, body, scopes, scope);
            }
        }
        e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
    };
}

#[cfg(test)]
mod tests {
    use base_db::{fixture::WithFixture, FileId, SourceDatabase};
    use hir_expand::{name::AsName, InFile};
    use syntax::{algo::find_node_at_offset, ast, AstNode};
    use test_utils::{assert_eq_text, extract_offset, mark};

    use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId};

    fn find_function(db: &TestDB, file_id: FileId) -> FunctionId {
        let krate = db.test_crate();
        let crate_def_map = db.crate_def_map(krate);

        let module = crate_def_map.modules_for_file(file_id).next().unwrap();
        let (_, def) = crate_def_map[module].scope.entries().next().unwrap();
        match def.take_values().unwrap() {
            ModuleDefId::FunctionId(it) => it,
            _ => panic!(),
        }
    }

    fn do_check(ra_fixture: &str, expected: &[&str]) {
        let (offset, code) = extract_offset(ra_fixture);
        let code = {
            let mut buf = String::new();
            let off: usize = offset.into();
            buf.push_str(&code[..off]);
            buf.push_str("$0marker");
            buf.push_str(&code[off..]);
            buf
        };

        let (db, position) = TestDB::with_position(&code);
        let file_id = position.file_id;
        let offset = position.offset;

        let file_syntax = db.parse(file_id).syntax_node();
        let marker: ast::PathExpr = find_node_at_offset(&file_syntax, offset).unwrap();
        let function = find_function(&db, file_id);

        let scopes = db.expr_scopes(function.into());
        let (_body, source_map) = db.body_with_source_map(function.into());

        let expr_id = source_map
            .node_expr(InFile { file_id: file_id.into(), value: &marker.into() })
            .unwrap();
        let scope = scopes.scope_for(expr_id);

        let actual = scopes
            .scope_chain(scope)
            .flat_map(|scope| scopes.entries(scope))
            .map(|it| it.name().to_string())
            .collect::<Vec<_>>()
            .join("\n");
        let expected = expected.join("\n");
        assert_eq_text!(&expected, &actual);
    }

    #[test]
    fn test_lambda_scope() {
        do_check(
            r"
            fn quux(foo: i32) {
                let f = |bar, baz: i32| {
                    $0
                };
            }",
            &["bar", "baz", "foo"],
        );
    }

    #[test]
    fn test_call_scope() {
        do_check(
            r"
            fn quux() {
                f(|x| $0 );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_method_call_scope() {
        do_check(
            r"
            fn quux() {
                z.f(|x| $0 );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_loop_scope() {
        do_check(
            r"
            fn quux() {
                loop {
                    let x = ();
                    $0
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_match() {
        do_check(
            r"
            fn quux() {
                match () {
                    Some(x) => {
                        $0
                    }
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_shadow_variable() {
        do_check(
            r"
            fn foo(x: String) {
                let x : &str = &x$0;
            }",
            &["x"],
        );
    }

    #[test]
    fn test_bindings_after_at() {
        do_check(
            r"
fn foo() {
    match Some(()) {
        opt @ Some(unit) => {
            $0
        }
        _ => {}
    }
}
",
            &["opt", "unit"],
        );
    }

    #[test]
    fn macro_inner_item() {
        do_check(
            r"
            macro_rules! mac {
                () => {{
                    fn inner() {}
                    inner();
                }};
            }

            fn foo() {
                mac!();
                $0
            }
        ",
            &[],
        );
    }

    #[test]
    fn broken_inner_item() {
        do_check(
            r"
            fn foo() {
                trait {}
                $0
            }
        ",
            &[],
        );
    }

    fn do_check_local_name(ra_fixture: &str, expected_offset: u32) {
        let (db, position) = TestDB::with_position(ra_fixture);
        let file_id = position.file_id;
        let offset = position.offset;

        let file = db.parse(file_id).ok().unwrap();
        let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
            .expect("failed to find a name at the target offset");
        let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), offset).unwrap();

        let function = find_function(&db, file_id);

        let scopes = db.expr_scopes(function.into());
        let (_body, source_map) = db.body_with_source_map(function.into());

        let expr_scope = {
            let expr_ast = name_ref.syntax().ancestors().find_map(ast::Expr::cast).unwrap();
            let expr_id =
                source_map.node_expr(InFile { file_id: file_id.into(), value: &expr_ast }).unwrap();
            scopes.scope_for(expr_id).unwrap()
        };

        let resolved = scopes.resolve_name_in_scope(expr_scope, &name_ref.as_name()).unwrap();
        let pat_src = source_map.pat_syntax(resolved.pat()).unwrap();

        let local_name = pat_src.value.either(
            |it| it.syntax_node_ptr().to_node(file.syntax()),
            |it| it.syntax_node_ptr().to_node(file.syntax()),
        );
        assert_eq!(local_name.text_range(), expected_name.syntax().text_range());
    }

    #[test]
    fn test_resolve_local_name() {
        do_check_local_name(
            r#"
fn foo(x: i32, y: u32) {
    {
        let z = x * 2;
    }
    {
        let t = x$0 * 3;
    }
}
"#,
            7,
        );
    }

    #[test]
    fn test_resolve_local_name_declaration() {
        do_check_local_name(
            r#"
fn foo(x: String) {
    let x : &str = &x$0;
}
"#,
            7,
        );
    }

    #[test]
    fn test_resolve_local_name_shadow() {
        do_check_local_name(
            r"
fn foo(x: String) {
    let x : &str = &x;
    x$0
}
",
            28,
        );
    }

    #[test]
    fn ref_patterns_contribute_bindings() {
        do_check_local_name(
            r"
fn foo() {
    if let Some(&from) = bar() {
        from$0;
    }
}
",
            28,
        );
    }

    #[test]
    fn while_let_desugaring() {
        mark::check!(infer_resolve_while_let);
        do_check_local_name(
            r#"
fn test() {
    let foo: Option<f32> = None;
    while let Option::Some(spam) = foo {
        spam$0
    }
}
"#,
            75,
        );
    }
}
<commit_msg>Add expression scopes for blocks<commit_after>//! Name resolution for expressions.
use std::sync::Arc;

use hir_expand::name::Name;
use la_arena::{Arena, Idx};
use rustc_hash::FxHashMap;

use crate::{
    body::Body,
    db::DefDatabase,
    expr::{Expr, ExprId, Pat, PatId, Statement},
    BlockId, DefWithBodyId,
};

pub type ScopeId = Idx<ScopeData>;

#[derive(Debug, PartialEq, Eq)]
pub struct ExprScopes {
    scopes: Arena<ScopeData>,
    scope_by_expr: FxHashMap<ExprId, ScopeId>,
}

#[derive(Debug, PartialEq, Eq)]
pub struct ScopeEntry {
    name: Name,
    pat: PatId,
}

impl ScopeEntry {
    pub fn name(&self) -> &Name {
        &self.name
    }

    pub fn pat(&self) -> PatId {
        self.pat
    }
}

#[derive(Debug, PartialEq, Eq)]
pub struct ScopeData {
    parent: Option<ScopeId>,
    block: Option<BlockId>,
    entries: Vec<ScopeEntry>,
}

impl ExprScopes {
    pub(crate) fn expr_scopes_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<ExprScopes> {
        let body = db.body(def);
        Arc::new(ExprScopes::new(&*body))
    }

    fn new(body: &Body) -> ExprScopes {
        let mut scopes =
            ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() };
        let root = scopes.root_scope();
        scopes.add_params_bindings(body, root, &body.params);
        compute_expr_scopes(body.body_expr, body, &mut scopes, root);
        scopes
    }

    pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
        &self.scopes[scope].entries
    }

    /// If `scope` refers to a block expression scope, returns the corresponding `BlockId`.
    pub fn block(&self, scope: ScopeId) -> Option<BlockId> {
        self.scopes[scope].block
    }

    pub fn scope_chain(&self, scope: Option<ScopeId>) -> impl Iterator<Item = ScopeId> + '_ {
        std::iter::successors(scope, move |&scope| self.scopes[scope].parent)
    }

    pub fn resolve_name_in_scope(&self, scope: ScopeId, name: &Name) -> Option<&ScopeEntry> {
        self.scope_chain(Some(scope))
            .find_map(|scope| self.entries(scope).iter().find(|it| it.name == *name))
    }

    pub fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
        self.scope_by_expr.get(&expr).copied()
    }

    pub fn scope_by_expr(&self) -> &FxHashMap<ExprId, ScopeId> {
        &self.scope_by_expr
    }

    fn root_scope(&mut self) -> ScopeId {
        self.scopes.alloc(ScopeData { parent: None, block: None, entries: vec![] })
    }

    fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
        self.scopes.alloc(ScopeData { parent: Some(parent), block: None, entries: vec![] })
    }

    fn new_block_scope(&mut self, parent: ScopeId, block: BlockId) -> ScopeId {
        self.scopes.alloc(ScopeData { parent: Some(parent), block: Some(block), entries: vec![] })
    }

    fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
        let pattern = &body[pat];
        if let Pat::Bind { name, .. } = pattern {
            let entry = ScopeEntry { name: name.clone(), pat };
            self.scopes[scope].entries.push(entry);
        }

        pattern.walk_child_pats(|pat| self.add_bindings(body, scope, pat));
    }

    fn add_params_bindings(&mut self, body: &Body, scope: ScopeId, params: &[PatId]) {
        params.iter().for_each(|pat| self.add_bindings(body, scope, *pat));
    }

    fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
        self.scope_by_expr.insert(node, scope);
    }
}

fn compute_block_scopes(
    statements: &[Statement],
    tail: Option<ExprId>,
    body: &Body,
    scopes: &mut ExprScopes,
    mut scope: ScopeId,
) {
    for stmt in statements {
        match stmt {
            Statement::Let { pat, initializer, .. } => {
                if let Some(expr) = initializer {
                    scopes.set_scope(*expr, scope);
                    compute_expr_scopes(*expr, body, scopes, scope);
                }
                scope = scopes.new_scope(scope);
                scopes.add_bindings(body, scope, *pat);
            }
            Statement::Expr(expr) => {
                scopes.set_scope(*expr, scope);
                compute_expr_scopes(*expr, body, scopes, scope);
            }
        }
    }
    if let Some(expr) = tail {
        compute_expr_scopes(expr, body, scopes, scope);
    }
}

fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) {
    scopes.set_scope(expr, scope);
    match &body[expr] {
        Expr::Block { statements, tail, id, .. } => {
            let scope = scopes.new_block_scope(scope, *id);
            // Overwrite the old scope for the block expr, so that every block scope can be found
            // via the block itself (important for blocks that only contain items, no expressions).
            scopes.set_scope(expr, scope);
            compute_block_scopes(&statements, *tail, body, scopes, scope);
        }
        Expr::For { iterable, pat, body: body_expr, .. } => {
            compute_expr_scopes(*iterable, body, scopes, scope);
            let scope = scopes.new_scope(scope);
            scopes.add_bindings(body, scope, *pat);
            compute_expr_scopes(*body_expr, body, scopes, scope);
        }
        Expr::Lambda { args, body: body_expr, .. } => {
            let scope = scopes.new_scope(scope);
            scopes.add_params_bindings(body, scope, &args);
            compute_expr_scopes(*body_expr, body, scopes, scope);
        }
        Expr::Match { expr, arms } => {
            compute_expr_scopes(*expr, body, scopes, scope);
            for arm in arms {
                let scope = scopes.new_scope(scope);
                scopes.add_bindings(body, scope, arm.pat);
                if let Some(guard) = arm.guard {
                    scopes.set_scope(guard, scope);
                    compute_expr_scopes(guard, body, scopes, scope);
                }
                scopes.set_scope(arm.expr, scope);
                compute_expr_scopes(arm.expr, body, scopes, scope);
            }
        }
        e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
    };
}

#[cfg(test)]
mod tests {
    use base_db::{fixture::WithFixture, FileId, SourceDatabase};
    use hir_expand::{name::AsName, InFile};
    use syntax::{algo::find_node_at_offset, ast, AstNode};
    use test_utils::{assert_eq_text, extract_offset, mark};

    use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId};

    fn find_function(db: &TestDB, file_id: FileId) -> FunctionId {
        let krate = db.test_crate();
        let crate_def_map = db.crate_def_map(krate);

        let module = crate_def_map.modules_for_file(file_id).next().unwrap();
        let (_, def) = crate_def_map[module].scope.entries().next().unwrap();
        match def.take_values().unwrap() {
            ModuleDefId::FunctionId(it) => it,
            _ => panic!(),
        }
    }

    fn do_check(ra_fixture: &str, expected: &[&str]) {
        let (offset, code) = extract_offset(ra_fixture);
        let code = {
            let mut buf = String::new();
            let off: usize = offset.into();
            buf.push_str(&code[..off]);
            buf.push_str("$0marker");
            buf.push_str(&code[off..]);
            buf
        };

        let (db, position) = TestDB::with_position(&code);
        let file_id = position.file_id;
        let offset = position.offset;

        let file_syntax = db.parse(file_id).syntax_node();
        let marker: ast::PathExpr = find_node_at_offset(&file_syntax, offset).unwrap();
        let function = find_function(&db, file_id);

        let scopes = db.expr_scopes(function.into());
        let (_body, source_map) = db.body_with_source_map(function.into());

        let expr_id = source_map
            .node_expr(InFile { file_id: file_id.into(), value: &marker.into() })
            .unwrap();
        let scope = scopes.scope_for(expr_id);

        let actual = scopes
            .scope_chain(scope)
            .flat_map(|scope| scopes.entries(scope))
            .map(|it| it.name().to_string())
            .collect::<Vec<_>>()
            .join("\n");
        let expected = expected.join("\n");
        assert_eq_text!(&expected, &actual);
    }

    #[test]
    fn test_lambda_scope() {
        do_check(
            r"
            fn quux(foo: i32) {
                let f = |bar, baz: i32| {
                    $0
                };
            }",
            &["bar", "baz", "foo"],
        );
    }

    #[test]
    fn test_call_scope() {
        do_check(
            r"
            fn quux() {
                f(|x| $0 );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_method_call_scope() {
        do_check(
            r"
            fn quux() {
                z.f(|x| $0 );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_loop_scope() {
        do_check(
            r"
            fn quux() {
                loop {
                    let x = ();
                    $0
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_match() {
        do_check(
            r"
            fn quux() {
                match () {
                    Some(x) => {
                        $0
                    }
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_shadow_variable() {
        do_check(
            r"
            fn foo(x: String) {
                let x : &str = &x$0;
            }",
            &["x"],
        );
    }

    #[test]
    fn test_bindings_after_at() {
        do_check(
            r"
fn foo() {
    match Some(()) {
        opt @ Some(unit) => {
            $0
        }
        _ => {}
    }
}
",
            &["opt", "unit"],
        );
    }

    #[test]
    fn macro_inner_item() {
        do_check(
            r"
            macro_rules! mac {
                () => {{
                    fn inner() {}
                    inner();
                }};
            }

            fn foo() {
                mac!();
                $0
            }
        ",
            &[],
        );
    }

    #[test]
    fn broken_inner_item() {
        do_check(
            r"
            fn foo() {
                trait {}
                $0
            }
        ",
            &[],
        );
    }

    fn do_check_local_name(ra_fixture: &str, expected_offset: u32) {
        let (db, position) = TestDB::with_position(ra_fixture);
        let file_id = position.file_id;
        let offset = position.offset;

        let file = db.parse(file_id).ok().unwrap();
        let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
            .expect("failed to find a name at the target offset");
        let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), offset).unwrap();

        let function = find_function(&db, file_id);

        let scopes = db.expr_scopes(function.into());
        let (_body, source_map) = db.body_with_source_map(function.into());

        let expr_scope = {
            let expr_ast = name_ref.syntax().ancestors().find_map(ast::Expr::cast).unwrap();
            let expr_id =
                source_map.node_expr(InFile { file_id: file_id.into(), value: &expr_ast }).unwrap();
            scopes.scope_for(expr_id).unwrap()
        };

        let resolved = scopes.resolve_name_in_scope(expr_scope, &name_ref.as_name()).unwrap();
        let pat_src = source_map.pat_syntax(resolved.pat()).unwrap();

        let local_name = pat_src.value.either(
            |it| it.syntax_node_ptr().to_node(file.syntax()),
            |it| it.syntax_node_ptr().to_node(file.syntax()),
        );
        assert_eq!(local_name.text_range(), expected_name.syntax().text_range());
    }

    #[test]
    fn test_resolve_local_name() {
        do_check_local_name(
            r#"
fn foo(x: i32, y: u32) {
    {
        let z = x * 2;
    }
    {
        let t = x$0 * 3;
    }
}
"#,
            7,
        );
    }

    #[test]
    fn test_resolve_local_name_declaration() {
        do_check_local_name(
            r#"
fn foo(x: String) {
    let x : &str = &x$0;
}
"#,
            7,
        );
    }

    #[test]
    fn test_resolve_local_name_shadow() {
        do_check_local_name(
            r"
fn foo(x: String) {
    let x : &str = &x;
    x$0
}
",
            28,
        );
    }

    #[test]
    fn ref_patterns_contribute_bindings() {
        do_check_local_name(
            r"
fn foo() {
    if let Some(&from) = bar() {
        from$0;
    }
}
",
            28,
        );
    }

    #[test]
    fn while_let_desugaring() {
        mark::check!(infer_resolve_while_let);
        do_check_local_name(
            r#"
fn test() {
    let foo: Option<f32> = None;
    while let Option::Some(spam) = foo {
        spam$0
    }
}
"#,
            75,
        );
    }
}
<|endoftext|>