<commit_before>//! This module contains `HashStable` implementations for various data types
//! from rustc::ty in no particular order.

use crate::ich::{Fingerprint, StableHashingContext, NodeIdHashingMode};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey,
                                           StableHasher, StableHasherResult};
use std::cell::RefCell;
use std::mem;
use crate::middle::region;
use crate::ty;
use crate::mir;

impl<'a, 'tcx, T> HashStable<StableHashingContext<'a>> for &'tcx ty::List<T>
where
    T: HashStable<StableHashingContext<'a>>,
{
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        thread_local! {
            static CACHE: RefCell<FxHashMap<(usize, usize), Fingerprint>> =
                RefCell::new(Default::default());
        }

        let hash = CACHE.with(|cache| {
            let key = (self.as_ptr() as usize, self.len());
            if let Some(&hash) = cache.borrow().get(&key) {
                return hash;
            }

            let mut hasher = StableHasher::new();
            (&self[..]).hash_stable(hcx, &mut hasher);

            let hash: Fingerprint = hasher.finish();
            cache.borrow_mut().insert(key, hash);
            hash
        });

        hash.hash_stable(hcx, hasher);
    }
}

impl<'a, 'tcx, T> ToStableHashKey<StableHashingContext<'a>> for &'tcx ty::List<T>
where
    T: HashStable<StableHashingContext<'a>>,
{
    type KeyType = Fingerprint;

    #[inline]
    fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> Fingerprint {
        let mut hasher = StableHasher::new();
        let mut hcx: StableHashingContext<'a> = hcx.clone();
        self.hash_stable(&mut hcx, &mut hasher);
        hasher.finish()
    }
}

impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::subst::Kind<'tcx> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        self.unpack().hash_stable(hcx, hasher);
    }
}

impl<'a> HashStable<StableHashingContext<'a>>
for ty::RegionKind {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        mem::discriminant(self).hash_stable(hcx, hasher);
        match *self {
            ty::ReErased |
            ty::ReStatic |
            ty::ReEmpty => {
                // No variant fields to hash for these ...
            }
            ty::ReLateBound(db, ty::BrAnon(i)) => {
                db.hash_stable(hcx, hasher);
                i.hash_stable(hcx, hasher);
            }
            ty::ReLateBound(db, ty::BrNamed(def_id, name)) => {
                db.hash_stable(hcx, hasher);
                def_id.hash_stable(hcx, hasher);
                name.hash_stable(hcx, hasher);
            }
            ty::ReLateBound(db, ty::BrEnv) => {
                db.hash_stable(hcx, hasher);
            }
            ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, index, name }) => {
                def_id.hash_stable(hcx, hasher);
                index.hash_stable(hcx, hasher);
                name.hash_stable(hcx, hasher);
            }
            ty::ReScope(scope) => {
                scope.hash_stable(hcx, hasher);
            }
            ty::ReFree(ref free_region) => {
                free_region.hash_stable(hcx, hasher);
            }
            ty::ReClosureBound(vid) => {
                vid.hash_stable(hcx, hasher);
            }
            ty::ReVar(..) |
            ty::RePlaceholder(..) => {
                bug!("StableHasher: unexpected region {:?}", *self)
            }
        }
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionVid {
    #[inline]
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        self.index().hash_stable(hcx, hasher);
    }
}

impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::ConstVid<'tcx> {
    #[inline]
    fn hash_stable<W: StableHasherResult>(
        &self,
        hcx: &mut StableHashingContext<'a>,
        hasher: &mut StableHasher<W>,
    ) {
        self.index.hash_stable(hcx, hasher);
    }
}

impl<'tcx> HashStable<StableHashingContext<'tcx>> for ty::BoundVar {
    #[inline]
    fn hash_stable<W: StableHasherResult>(
        &self,
        hcx: &mut StableHashingContext<'tcx>,
        hasher: &mut StableHasher<W>,
    ) {
        self.index().hash_stable(hcx, hasher);
    }
}

impl<'a, T> HashStable<StableHashingContext<'a>> for ty::Binder<T>
where
    T: HashStable<StableHashingContext<'a>>,
{
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        self.skip_binder().hash_stable(hcx, hasher);
    }
}

// AllocIds get resolved to whatever they point to (to be stable)
impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::AllocId {
    fn hash_stable<W: StableHasherResult>(
        &self,
        hcx: &mut StableHashingContext<'a>,
        hasher: &mut StableHasher<W>,
    ) {
        ty::tls::with_opt(|tcx| {
            trace!("hashing {:?}", *self);
            let tcx = tcx.expect("can't hash AllocIds during hir lowering");
            let alloc_kind = tcx.alloc_map.lock().get(*self);
            alloc_kind.hash_stable(hcx, hasher);
        });
    }
}

// Allocations treat their relocations specially
impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::Allocation {
    fn hash_stable<W: StableHasherResult>(
        &self,
        hcx: &mut StableHashingContext<'a>,
        hasher: &mut StableHasher<W>,
    ) {
        let mir::interpret::Allocation {
            bytes, relocations, undef_mask, align, mutability,
            extra: _,
        } = self;
        bytes.hash_stable(hcx, hasher);
        for reloc in relocations.iter() {
            reloc.hash_stable(hcx, hasher);
        }
        undef_mask.hash_stable(hcx, hasher);
        align.hash_stable(hcx, hasher);
        mutability.hash_stable(hcx, hasher);
    }
}

impl_stable_hash_for!(enum ::syntax::ast::Mutability {
    Immutable,
    Mutable
});

impl<'a> ToStableHashKey<StableHashingContext<'a>> for region::Scope {
    type KeyType = region::Scope;

    #[inline]
    fn to_stable_hash_key(&self, _: &StableHashingContext<'a>) -> region::Scope {
        *self
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::TyVid {
    fn hash_stable<W: StableHasherResult>(&self,
                                          _hcx: &mut StableHashingContext<'a>,
                                          _hasher: &mut StableHasher<W>) {
        // TyVid values are confined to an inference context and hence
        // should not be hashed.
        bug!("ty::TyKind::hash_stable() - can't hash a TyVid {:?}.", *self)
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::IntVid {
    fn hash_stable<W: StableHasherResult>(&self,
                                          _hcx: &mut StableHashingContext<'a>,
                                          _hasher: &mut StableHasher<W>) {
        // IntVid values are confined to an inference context and hence
        // should not be hashed.
        bug!("ty::TyKind::hash_stable() - can't hash an IntVid {:?}.", *self)
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::FloatVid {
    fn hash_stable<W: StableHasherResult>(&self,
                                          _hcx: &mut StableHashingContext<'a>,
                                          _hasher: &mut StableHasher<W>) {
        // FloatVid values are confined to an inference context and hence
        // should not be hashed.
        bug!("ty::TyKind::hash_stable() - can't hash a FloatVid {:?}.", *self)
    }
}

impl<'a, T> HashStable<StableHashingContext<'a>> for ty::steal::Steal<T>
where
    T: HashStable<StableHashingContext<'a>>,
{
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        self.borrow().hash_stable(hcx, hasher);
    }
}

impl<'a> HashStable<StableHashingContext<'a>>
for crate::middle::privacy::AccessLevels {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
            let crate::middle::privacy::AccessLevels {
                ref map
            } = *self;

            map.hash_stable(hcx, hasher);
        });
    }
}
<commit_msg>Further reduce the likelyhood of hash collisions<commit_after>//! This module contains `HashStable` implementations for various data types
//! from rustc::ty in no particular order.

use crate::ich::{Fingerprint, StableHashingContext, NodeIdHashingMode};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey,
                                           StableHasher, StableHasherResult};
use std::cell::RefCell;
use std::mem;
use crate::middle::region;
use crate::ty;
use crate::mir;

impl<'a, 'tcx, T> HashStable<StableHashingContext<'a>> for &'tcx ty::List<T>
where
    T: HashStable<StableHashingContext<'a>>,
{
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        thread_local! {
            static CACHE: RefCell<FxHashMap<(usize, usize), Fingerprint>> =
                RefCell::new(Default::default());
        }

        let hash = CACHE.with(|cache| {
            let key = (self.as_ptr() as usize, self.len());
            if let Some(&hash) = cache.borrow().get(&key) {
                return hash;
            }

            let mut hasher = StableHasher::new();
            (&self[..]).hash_stable(hcx, &mut hasher);

            let hash: Fingerprint = hasher.finish();
            cache.borrow_mut().insert(key, hash);
            hash
        });

        hash.hash_stable(hcx, hasher);
    }
}

impl<'a, 'tcx, T> ToStableHashKey<StableHashingContext<'a>> for &'tcx ty::List<T>
where
    T: HashStable<StableHashingContext<'a>>,
{
    type KeyType = Fingerprint;

    #[inline]
    fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> Fingerprint {
        let mut hasher = StableHasher::new();
        let mut hcx: StableHashingContext<'a> = hcx.clone();
        self.hash_stable(&mut hcx, &mut hasher);
        hasher.finish()
    }
}

impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::subst::Kind<'tcx> {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        self.unpack().hash_stable(hcx, hasher);
    }
}

impl<'a> HashStable<StableHashingContext<'a>>
for ty::RegionKind {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        mem::discriminant(self).hash_stable(hcx, hasher);
        match *self {
            ty::ReErased |
            ty::ReStatic |
            ty::ReEmpty => {
                // No variant fields to hash for these ...
            }
            ty::ReLateBound(db, ty::BrAnon(i)) => {
                db.hash_stable(hcx, hasher);
                i.hash_stable(hcx, hasher);
            }
            ty::ReLateBound(db, ty::BrNamed(def_id, name)) => {
                db.hash_stable(hcx, hasher);
                def_id.hash_stable(hcx, hasher);
                name.hash_stable(hcx, hasher);
            }
            ty::ReLateBound(db, ty::BrEnv) => {
                db.hash_stable(hcx, hasher);
            }
            ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, index, name }) => {
                def_id.hash_stable(hcx, hasher);
                index.hash_stable(hcx, hasher);
                name.hash_stable(hcx, hasher);
            }
            ty::ReScope(scope) => {
                scope.hash_stable(hcx, hasher);
            }
            ty::ReFree(ref free_region) => {
                free_region.hash_stable(hcx, hasher);
            }
            ty::ReClosureBound(vid) => {
                vid.hash_stable(hcx, hasher);
            }
            ty::ReVar(..) |
            ty::RePlaceholder(..) => {
                bug!("StableHasher: unexpected region {:?}", *self)
            }
        }
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionVid {
    #[inline]
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        self.index().hash_stable(hcx, hasher);
    }
}

impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::ConstVid<'tcx> {
    #[inline]
    fn hash_stable<W: StableHasherResult>(
        &self,
        hcx: &mut StableHashingContext<'a>,
        hasher: &mut StableHasher<W>,
    ) {
        self.index.hash_stable(hcx, hasher);
    }
}

impl<'tcx> HashStable<StableHashingContext<'tcx>> for ty::BoundVar {
    #[inline]
    fn hash_stable<W: StableHasherResult>(
        &self,
        hcx: &mut StableHashingContext<'tcx>,
        hasher: &mut StableHasher<W>,
    ) {
        self.index().hash_stable(hcx, hasher);
    }
}

impl<'a, T> HashStable<StableHashingContext<'a>> for ty::Binder<T>
where
    T: HashStable<StableHashingContext<'a>>,
{
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        self.skip_binder().hash_stable(hcx, hasher);
    }
}

// AllocIds get resolved to whatever they point to (to be stable)
impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::AllocId {
    fn hash_stable<W: StableHasherResult>(
        &self,
        hcx: &mut StableHashingContext<'a>,
        hasher: &mut StableHasher<W>,
    ) {
        ty::tls::with_opt(|tcx| {
            trace!("hashing {:?}", *self);
            let tcx = tcx.expect("can't hash AllocIds during hir lowering");
            let alloc_kind = tcx.alloc_map.lock().get(*self);
            alloc_kind.hash_stable(hcx, hasher);
        });
    }
}

// Allocations treat their relocations specially
impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::Allocation {
    fn hash_stable<W: StableHasherResult>(
        &self,
        hcx: &mut StableHashingContext<'a>,
        hasher: &mut StableHasher<W>,
    ) {
        let mir::interpret::Allocation {
            bytes, relocations, undef_mask, align, mutability,
            extra: _,
        } = self;
        bytes.hash_stable(hcx, hasher);
        relocations.len().hash_stable(hcx, hasher);
        for reloc in relocations.iter() {
            reloc.hash_stable(hcx, hasher);
        }
        undef_mask.hash_stable(hcx, hasher);
        align.hash_stable(hcx, hasher);
        mutability.hash_stable(hcx, hasher);
    }
}

impl_stable_hash_for!(enum ::syntax::ast::Mutability {
    Immutable,
    Mutable
});

impl<'a> ToStableHashKey<StableHashingContext<'a>> for region::Scope {
    type KeyType = region::Scope;

    #[inline]
    fn to_stable_hash_key(&self, _: &StableHashingContext<'a>) -> region::Scope {
        *self
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::TyVid {
    fn hash_stable<W: StableHasherResult>(&self,
                                          _hcx: &mut StableHashingContext<'a>,
                                          _hasher: &mut StableHasher<W>) {
        // TyVid values are confined to an inference context and hence
        // should not be hashed.
        bug!("ty::TyKind::hash_stable() - can't hash a TyVid {:?}.", *self)
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::IntVid {
    fn hash_stable<W: StableHasherResult>(&self,
                                          _hcx: &mut StableHashingContext<'a>,
                                          _hasher: &mut StableHasher<W>) {
        // IntVid values are confined to an inference context and hence
        // should not be hashed.
        bug!("ty::TyKind::hash_stable() - can't hash an IntVid {:?}.", *self)
    }
}

impl<'a> HashStable<StableHashingContext<'a>> for ty::FloatVid {
    fn hash_stable<W: StableHasherResult>(&self,
                                          _hcx: &mut StableHashingContext<'a>,
                                          _hasher: &mut StableHasher<W>) {
        // FloatVid values are confined to an inference context and hence
        // should not be hashed.
        bug!("ty::TyKind::hash_stable() - can't hash a FloatVid {:?}.", *self)
    }
}

impl<'a, T> HashStable<StableHashingContext<'a>> for ty::steal::Steal<T>
where
    T: HashStable<StableHashingContext<'a>>,
{
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        self.borrow().hash_stable(hcx, hasher);
    }
}

impl<'a> HashStable<StableHashingContext<'a>>
for crate::middle::privacy::AccessLevels {
    fn hash_stable<W: StableHasherResult>(&self,
                                          hcx: &mut StableHashingContext<'a>,
                                          hasher: &mut StableHasher<W>) {
        hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
            let crate::middle::privacy::AccessLevels {
                ref map
            } = *self;

            map.hash_stable(hcx, hasher);
        });
    }
}
<|endoftext|>


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

use driver::session;
use driver::session::Session;
use syntax::ast::{Crate, NodeId, item, item_fn};
use syntax::ast_map;
use syntax::attr;
use syntax::codemap::span;
use syntax::oldvisit::{default_visitor, mk_vt, vt, Visitor, visit_crate};
use syntax::oldvisit::{visit_item};
use syntax::parse::token::special_idents;
use std::util;

struct EntryContext {
    session: Session,

    ast_map: ast_map::map,

    // The top-level function called 'main'
    main_fn: Option<(NodeId, span)>,

    // The function that has attribute named 'main'
    attr_main_fn: Option<(NodeId, span)>,

    // The function that has the attribute 'start' on it
    start_fn: Option<(NodeId, span)>,

    // The functions that one might think are 'main' but aren't, e.g.
    // main functions not defined at the top level. For diagnostics.
    non_main_fns: ~[(NodeId, span)],
}

type EntryVisitor = vt<@mut EntryContext>;

pub fn find_entry_point(session: Session, crate: &Crate, ast_map: ast_map::map) {

    // FIXME #4404 android JNI hacks
    if *session.building_library &&
        session.targ_cfg.os != session::os_android {
        // No need to find a main function
        return;
    }

    // If the user wants no main function at all, then stop here.
    if attr::contains_name(crate.attrs, "no_main") {
        *session.entry_type = Some(session::EntryNone);
        return
    }

    let ctxt = @mut EntryContext {
        session: session,
        ast_map: ast_map,
        main_fn: None,
        attr_main_fn: None,
        start_fn: None,
        non_main_fns: ~[],
    };

    visit_crate(crate, (ctxt, mk_vt(@Visitor {
        visit_item: |item, (ctxt, visitor)| find_item(item, ctxt, visitor),
        .. *default_visitor()
    })));

    configure_main(ctxt);
}

fn find_item(item: @item, ctxt: @mut EntryContext, visitor: EntryVisitor) {
    match item.node {
        item_fn(*) => {
            if item.ident == special_idents::main {
                match ctxt.ast_map.find(&item.id) {
                    Some(&ast_map::node_item(_, path)) => {
                        if path.len() == 0 {
                            // This is a top-level function so can be 'main'
                            if ctxt.main_fn.is_none() {
                                ctxt.main_fn = Some((item.id, item.span));
                            } else {
                                ctxt.session.span_err(
                                    item.span,
                                    "multiple 'main' functions");
                            }
                        } else {
                            // This isn't main
                            ctxt.non_main_fns.push((item.id, item.span));
                        }
                    }
                    _ => util::unreachable()
                }
            }

            if attr::contains_name(item.attrs, "main") {
                if ctxt.attr_main_fn.is_none() {
                    ctxt.attr_main_fn = Some((item.id, item.span));
                } else {
                    ctxt.session.span_err(
                        item.span,
                        "multiple 'main' functions");
                }
            }

            if attr::contains_name(item.attrs, "start") {
                if ctxt.start_fn.is_none() {
                    ctxt.start_fn = Some((item.id, item.span));
                } else {
                    ctxt.session.span_err(
                        item.span,
                        "multiple 'start' functions");
                }
            }
        }
        _ => ()
    }

    visit_item(item, (ctxt, visitor));
}

fn configure_main(ctxt: @mut EntryContext) {
    let this = &mut *ctxt;
    if this.start_fn.is_some() {
        *this.session.entry_fn = this.start_fn;
        *this.session.entry_type = Some(session::EntryStart);
    } else if this.attr_main_fn.is_some() {
        *this.session.entry_fn = this.attr_main_fn;
        *this.session.entry_type = Some(session::EntryMain);
    } else if this.main_fn.is_some() {
        *this.session.entry_fn = this.main_fn;
        *this.session.entry_type = Some(session::EntryMain);
    } else {
        if !*this.session.building_library {
            // No main function
            this.session.err("main function not found");
            if !this.non_main_fns.is_empty() {
                // There were some functions named 'main' though. Try to give the user a hint.
                this.session.note("the main function must be defined at the crate level \
                                   but you have one or more functions named 'main' that are not \
                                   defined at the crate level. Either move the definition or \
                                   attach the `#[main]` attribute to override this behavior.");
                for &(_, span) in this.non_main_fns.iter() {
                    this.session.span_note(span, "here is a function named 'main'");
                }
            }
            this.session.abort_if_errors();
        } else {
            // If we *are* building a library, then we're on android where we still might
            // optionally want to translate main $4404
            assert_eq!(this.session.targ_cfg.os, session::os_android);
        }
    }
}
<commit_msg>Port entry.rs from oldvisit to <V:Visitor> trait API.<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use driver::session;
use driver::session::Session;
use syntax::ast::{Crate, NodeId, item, item_fn};
use syntax::ast_map;
use syntax::attr;
use syntax::codemap::span;
use syntax::parse::token::special_idents;
use syntax::visit;
use syntax::visit::Visitor;
use std::util;

struct EntryContext {
    session: Session,

    ast_map: ast_map::map,

    // The top-level function called 'main'
    main_fn: Option<(NodeId, span)>,

    // The function that has attribute named 'main'
    attr_main_fn: Option<(NodeId, span)>,

    // The function that has the attribute 'start' on it
    start_fn: Option<(NodeId, span)>,

    // The functions that one might think are 'main' but aren't, e.g.
    // main functions not defined at the top level. For diagnostics.
    non_main_fns: ~[(NodeId, span)],
}

struct EntryVisitor;

impl Visitor<@mut EntryContext> for EntryVisitor {
    fn visit_item(&mut self, item:@item, ctxt:@mut EntryContext) {
        find_item(item, ctxt, self);
    }
}

pub fn find_entry_point(session: Session, crate: &Crate, ast_map: ast_map::map) {

    // FIXME #4404 android JNI hacks
    if *session.building_library &&
        session.targ_cfg.os != session::os_android {
        // No need to find a main function
        return;
    }

    // If the user wants no main function at all, then stop here.
    if attr::contains_name(crate.attrs, "no_main") {
        *session.entry_type = Some(session::EntryNone);
        return
    }

    let ctxt = @mut EntryContext {
        session: session,
        ast_map: ast_map,
        main_fn: None,
        attr_main_fn: None,
        start_fn: None,
        non_main_fns: ~[],
    };

    let mut v = EntryVisitor;

    visit::walk_crate(&mut v, crate, ctxt);

    configure_main(ctxt);
}

fn find_item(item: @item, ctxt: @mut EntryContext, visitor: &mut EntryVisitor) {
    match item.node {
        item_fn(*) => {
            if item.ident == special_idents::main {
                match ctxt.ast_map.find(&item.id) {
                    Some(&ast_map::node_item(_, path)) => {
                        if path.len() == 0 {
                            // This is a top-level function so can be 'main'
                            if ctxt.main_fn.is_none() {
                                ctxt.main_fn = Some((item.id, item.span));
                            } else {
                                ctxt.session.span_err(
                                    item.span,
                                    "multiple 'main' functions");
                            }
                        } else {
                            // This isn't main
                            ctxt.non_main_fns.push((item.id, item.span));
                        }
                    }
                    _ => util::unreachable()
                }
            }

            if attr::contains_name(item.attrs, "main") {
                if ctxt.attr_main_fn.is_none() {
                    ctxt.attr_main_fn = Some((item.id, item.span));
                } else {
                    ctxt.session.span_err(
                        item.span,
                        "multiple 'main' functions");
                }
            }

            if attr::contains_name(item.attrs, "start") {
                if ctxt.start_fn.is_none() {
                    ctxt.start_fn = Some((item.id, item.span));
                } else {
                    ctxt.session.span_err(
                        item.span,
                        "multiple 'start' functions");
                }
            }
        }
        _ => ()
    }

    visit::walk_item(visitor, item, ctxt);
}

fn configure_main(ctxt: @mut EntryContext) {
    let this = &mut *ctxt;
    if this.start_fn.is_some() {
        *this.session.entry_fn = this.start_fn;
        *this.session.entry_type = Some(session::EntryStart);
    } else if this.attr_main_fn.is_some() {
        *this.session.entry_fn = this.attr_main_fn;
        *this.session.entry_type = Some(session::EntryMain);
    } else if this.main_fn.is_some() {
        *this.session.entry_fn = this.main_fn;
        *this.session.entry_type = Some(session::EntryMain);
    } else {
        if !*this.session.building_library {
            // No main function
            this.session.err("main function not found");
            if !this.non_main_fns.is_empty() {
                // There were some functions named 'main' though. Try to give the user a hint.
                this.session.note("the main function must be defined at the crate level \
                                   but you have one or more functions named 'main' that are not \
                                   defined at the crate level. Either move the definition or \
                                   attach the `#[main]` attribute to override this behavior.");
                for &(_, span) in this.non_main_fns.iter() {
                    this.session.span_note(span, "here is a function named 'main'");
                }
            }
            this.session.abort_if_errors();
        } else {
            // If we *are* building a library, then we're on android where we still might
            // optionally want to translate main $4404
            assert_eq!(this.session.targ_cfg.os, session::os_android);
        }
    }
}
<|endoftext|>


<commit_before>#![feature(test)]

extern crate test;

#[cfg(test)]
mod tests {
    use shogi::bitboard::Factory;
    use shogi::{Color, Position, Square};
    use test::Bencher;

    #[bench]
    fn perft(b: &mut Bencher) {
        Factory::init();

        let mut pos = Position::new();
        pos.set_sfen("l6nl/5+P1gk/2np1S3/p1p4Pp/3P2Sp1/1PPb2P1P/P5GS1/R8/LN4bKL w RGgsn5p 1")
            .expect("failed to parse SFEN string");

        b.iter(|| {
            let mut cnt = 0;

            for sq in Square::iter() {
                let pc = pos.piece_at(sq);

                if let Some(ref pc) = *pc {
                    if pc.color == Color::White {
                        let bb = pos.move_candidates(sq, pc);
                        cnt += bb.count();
                    }
                }
            }

            assert_eq!(32, cnt);
        });
    }
}
<commit_msg>fix: broken compilation in perft (#31)<commit_after>#![feature(test)]

extern crate test;

#[cfg(test)]
mod tests {
    use shogi::bitboard::Factory;
    use shogi::{Color, Position, Square};
    use test::Bencher;

    #[bench]
    fn perft(b: &mut Bencher) {
        Factory::init();

        let mut pos = Position::new();
        pos.set_sfen("l6nl/5+P1gk/2np1S3/p1p4Pp/3P2Sp1/1PPb2P1P/P5GS1/R8/LN4bKL w RGgsn5p 1")
            .expect("failed to parse SFEN string");

        b.iter(|| {
            let mut cnt = 0;

            for sq in Square::iter() {
                let pc = pos.piece_at(sq);

                if let Some(ref pc) = *pc {
                    if pc.color == Color::White {
                        let bb = pos.move_candidates(sq, *pc);
                        cnt += bb.count();
                    }
                }
            }

            assert_eq!(32, cnt);
        });
    }
}
<|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>Fix documentation links for EventTarget<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>use definitions::ImageBuilder;
use definitions::error::*;
use definitions::typedef::*;
use mnemonic::Mnemonic;
use regex::Regex;
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
use std::path::{Path, PathBuf};

lazy_static! {
    static ref LABELED_MNEMONIC_RE: Regex = Regex::new(r"^\.(.+?) +?(.+)$").unwrap();
    static ref LABEL_RE: Regex = Regex::new(r"^\.([^\s]+)$").unwrap();
    static ref INCLUDE_RE: Regex = Regex::new(r"^include! +([^\s]+)$").unwrap();
}

#[derive(Default)]
struct BASMCompiler {
    label_addr_map: HashMap<String, Address>,
    mnemonics: Vec<Mnemonic>,
    builder: ImageBuilder,
    last_base_dir: Option<PathBuf>,
}

impl BASMCompiler {
    fn add_label(&mut self, label: String) {
        self.label_addr_map.entry(label).or_insert(self.mnemonics.len());
    }

    fn compile_mnemonics(&mut self, file_name: String) -> Result<()> {
        let orig_path = Path::new(&file_name);

        let path = if orig_path.is_relative() {
            let file_name = if let Some(ref file_name) = orig_path.file_name() {
                if let Some(..) = self.last_base_dir {
                    PathBuf::from(orig_path)
                } else {
                    PathBuf::from(file_name.clone())
                }
            } else {
                bail!("unable to obtain file name");
            };

            let absolute_path = if let Some(ref base_dir) = self.last_base_dir {
                base_dir.clone()
            } else {
                let current_dir = env::current_dir()
                    .chain_err(|| "unable to get current directory")?;
                let base_file = current_dir.join(orig_path);
                let base_dir = if let Some(ref dir) = base_file.parent() {
                    dir.clone()
                } else {
                    bail!("unable to get parent directory")
                };

                self.last_base_dir = Some(base_dir.to_path_buf().clone());
                base_dir.to_path_buf()
            };

            absolute_path.join(file_name)
        } else {
            orig_path.clone().to_path_buf()
        };

        println!("{:?}", path);

        let file = File::open(path).chain_err(|| "unable to open file")?;

        let reader = BufReader::new(file);

        for line in reader.lines() {
            let line = line.chain_err(|| "unable to read line")?;

            if line.is_empty() {
                continue;
            }

            let mut r_split = line.rsplitn(2, ';');

            if r_split.clone().count() > 1 {
                r_split.next();
            }

            if let Some(first_half) = r_split.next() {
                let first_half = first_half.trim();

                let first_half: String = if LABELED_MNEMONIC_RE.is_match(first_half) {
                    let captures = if let Some(captures) = LABELED_MNEMONIC_RE
                           .captures_iter(first_half)
                           .next() {
                        captures
                    } else {
                        bail!("no label capture found")
                    };

                    let label = captures[1].trim();

                    println!(
                        "Label {:?} found at address {}",
                        label,
                        self.mnemonics.len()
                    );

                    self.add_label(label.to_owned());

                    captures[2].trim().to_owned()
                } else if LABEL_RE.is_match(first_half) {
                    let captures =
                        if let Some(captures) = LABEL_RE.captures_iter(first_half).next() {
                            captures
                        } else {
                            bail!("no label capture found")
                        };

                    let label = captures[1].trim();

                    println!(
                        "Label {:?} found at address {}",
                        label,
                        self.mnemonics.len()
                    );

                    self.add_label(label.to_owned());
                    continue;
                } else if INCLUDE_RE.is_match(first_half) {
                    let captures =
                        if let Some(captures) = INCLUDE_RE.captures_iter(first_half).next() {
                            captures
                        } else {
                            bail!("no include capture found")
                        };

                    self.compile_mnemonics(captures[1].trim().to_owned() + ".basm")?;

                    continue;
                } else {
                    first_half.to_owned()
                };

                let mut first_half_split = first_half.splitn(2, ' ');

                if let Some(opcode) = first_half_split.next() {
                    let opcode = opcode.trim().to_lowercase();

                    let args: Vec<String> = if let Some(args) = first_half_split.next() {
                        args.split(',').map(|arg| arg.trim().to_owned()).collect()
                    } else {
                        Vec::new()
                    };

                    println!("Adding mnemonic {:?} with args: {:?}", opcode, args);

                    self.mnemonics.push(text_to_mnemonic(opcode, args)?);
                } else {
                    bail!("opcode expected. Found {:?}", first_half_split);
                }
            } else {
                bail!("instruction expected. Found {:?}", line.to_string());
            }
        }

        Ok(())
    }

    fn compile_mnemonic(&mut self, mnemonic: Mnemonic) -> Result<()> {

        println!("Compiling mnemonic {:?}", mnemonic);

        match mnemonic {
            Mnemonic::Add(dest, src) => self.builder.add(dest, src),
            Mnemonic::Sub(dest, src) => self.builder.sub(dest, src),
            Mnemonic::Div(dest, src) => self.builder.div(dest, src),
            Mnemonic::Mul(dest, src) => self.builder.mul(dest, src),
            Mnemonic::Rem(dest, src) => self.builder.rem(dest, src),

            Mnemonic::Cmp(target_a, target_b) => self.builder.cmp(target_a, target_b),
            Mnemonic::Jmp(label) => {
                let addr = self.lookup(&label)?;
                self.builder.jmp(addr);
            }
            Mnemonic::JmpLt(label) => {
                let addr = self.lookup(&label)?;
                self.builder.jmp_lt(addr);
            }
            Mnemonic::JmpGt(label) => {
                let addr = self.lookup(&label)?;
                self.builder.jmp_gt(addr);
            }
            Mnemonic::JmpEq(label) => {
                let addr = self.lookup(&label)?;
                self.builder.jmp_eq(addr);
            }
            Mnemonic::JmpLtEq(label) => {
                let addr = self.lookup(&label)?;
                self.builder.jmp_lt_eq(addr);
            }
            Mnemonic::JmpGtEq(label) => {
                let addr = self.lookup(&label)?;
                self.builder.jmp_gt_eq(addr);
            }

            Mnemonic::Cast(target, type_t) => self.builder.cast(target, type_t),

            Mnemonic::Push(target, value) => self.builder.push(target, value),
            Mnemonic::Mov(dest, src) => self.builder.mov(dest, src),
            Mnemonic::Swp(target_a, target_b) => self.builder.swp(target_a, target_b),
            Mnemonic::Dup(target) => self.builder.dup(target),

            Mnemonic::Call(label) => {
                let addr = self.lookup(&label)?;
                self.builder.call(addr);
            }
            Mnemonic::Ret => self.builder.ret(),

            Mnemonic::Halt => self.builder.halt(),
            Mnemonic::Pause => self.builder.pause(),
            Mnemonic::Nop => self.builder.nop(),
            Mnemonic::Int(int) => self.builder.int(int),
        }

        Ok(())
    }

    fn lookup(&mut self, input: &String) -> Result<Address> {
        if let Some(addr) = self.label_addr_map.get(input) {
            Ok(*addr)
        } else {
            bail!("label {:?} not found", input);
        }
    }

    pub fn compile(&mut self, file_name: String) -> Result<ImageData> {
        self.compile_mnemonics(file_name)?;

        for mnemonic in self.mnemonics.clone() {
            self.compile_mnemonic(mnemonic)?;
        }

        Ok(self.builder.clone().gen())
    }
}

fn text_to_mnemonic(opcode: String, args: Vec<String>) -> Result<Mnemonic> {
    match opcode.as_str() {
        "add" => Ok(Mnemonic::Add(args[0].parse()?, args[1].parse()?)),
        "sub" => Ok(Mnemonic::Sub(args[0].parse()?, args[1].parse()?)),
        "div" => Ok(Mnemonic::Div(args[0].parse()?, args[1].parse()?)),
        "mul" => Ok(Mnemonic::Mul(args[0].parse()?, args[1].parse()?)),
        "rem" => Ok(Mnemonic::Rem(args[0].parse()?, args[1].parse()?)),

        "cmp" => Ok(Mnemonic::Cmp(args[0].parse()?, args[1].parse()?)),
        "jmp" => Ok(Mnemonic::Jmp(args[0].parse()?)),
        "jmplt" => Ok(Mnemonic::JmpLt(args[0].parse()?)),
        "jmpgt" => Ok(Mnemonic::JmpGt(args[0].parse()?)),
        "jmpeq" => Ok(Mnemonic::JmpEq(args[0].parse()?)),
        "jmplteq" => Ok(Mnemonic::JmpLtEq(args[0].parse()?)),
        "jmpgteq" => Ok(Mnemonic::JmpGtEq(args[0].parse()?)),

        "cast" => Ok(Mnemonic::Cast(args[0].parse()?, args[1].parse()?)),
        "push" => Ok(Mnemonic::Push(args[0].parse()?, args[1].parse()?)),
        "mov" => Ok(Mnemonic::Mov(args[0].parse()?, args[1].parse()?)),
        "swp" => Ok(Mnemonic::Swp(args[0].parse()?, args[1].parse()?)),
        "dup" => Ok(Mnemonic::Dup(args[0].parse()?)),

        "call" => Ok(Mnemonic::Call(args[0].parse()?)),
        "ret" => Ok(Mnemonic::Ret),

        "halt" => Ok(Mnemonic::Halt),
        "pause" => Ok(Mnemonic::Pause),
        "nop" => Ok(Mnemonic::Nop),
        "int" => Ok(Mnemonic::Int(args[0].parse()?)),
        _ => bail!("unkwnown opcode {:?}", opcode),
    }
}

pub fn compile(file_name: String) -> Result<ImageData> {
    BASMCompiler::default().compile(file_name)
}

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

    #[test]
    fn labeled_mnemonic_regex() {
        let input = ".start mov $st, $vi(19)";

        if !LABELED_MNEMONIC_RE.is_match(input) {
            panic!("input doesn't match a labeled mnemonic");
        } else {
            let captures = LABELED_MNEMONIC_RE.captures_iter(input).next().unwrap();

            assert_eq!(captures[1].trim(), "start");
            assert_eq!(captures[2].trim(), "mov $st, $vi(19)");
        }
    }

    #[test]
    fn label_regex() {
        let input = ".start";

        if !LABEL_RE.is_match(input) {
            panic!("input doesn't match a label");
        } else {
            let captures = LABEL_RE.captures_iter(input).next().unwrap();

            assert_eq!(captures[1].trim(), "start");
        }
    }

    #[test]
    fn include_regex() {
        let input = "include! std";

        if !INCLUDE_RE.is_match(input) {
            panic!("input doesn't match an include statement");
        } else {
            let captures = INCLUDE_RE.captures_iter(input).next().unwrap();

            assert_eq!(captures[1].trim(), "std");
        }
    }
}
<commit_msg>Removed unused print<commit_after>use definitions::ImageBuilder;
use definitions::error::*;
use definitions::typedef::*;
use mnemonic::Mnemonic;
use regex::Regex;
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
use std::path::{Path, PathBuf};

lazy_static! {
    static ref LABELED_MNEMONIC_RE: Regex = Regex::new(r"^\.(.+?) +?(.+)$").unwrap();
    static ref LABEL_RE: Regex = Regex::new(r"^\.([^\s]+)$").unwrap();
    static ref INCLUDE_RE: Regex = Regex::new(r"^include! +([^\s]+)$").unwrap();
}

#[derive(Default)]
struct BASMCompiler {
    label_addr_map: HashMap<String, Address>,
    mnemonics: Vec<Mnemonic>,
    builder: ImageBuilder,
    last_base_dir: Option<PathBuf>,
}

impl BASMCompiler {
    fn add_label(&mut self, label: String) {
        self.label_addr_map.entry(label).or_insert(self.mnemonics.len());
    }

    fn compile_mnemonics(&mut self, file_name: String) -> Result<()> {
        let orig_path = Path::new(&file_name);

        let path = if orig_path.is_relative() {
            let file_name = if let Some(ref file_name) = orig_path.file_name() {
                if let Some(..) = self.last_base_dir {
                    PathBuf::from(orig_path)
                } else {
                    PathBuf::from(file_name.clone())
                }
            } else {
                bail!("unable to obtain file name");
            };

            let absolute_path = if let Some(ref base_dir) = self.last_base_dir {
                base_dir.clone()
            } else {
                let current_dir = env::current_dir()
                    .chain_err(|| "unable to get current directory")?;
                let base_file = current_dir.join(orig_path);
                let base_dir = if let Some(ref dir) = base_file.parent() {
                    dir.clone()
                } else {
                    bail!("unable to get parent directory")
                };

                self.last_base_dir = Some(base_dir.to_path_buf().clone());
                base_dir.to_path_buf()
            };

            absolute_path.join(file_name)
        } else {
            orig_path.clone().to_path_buf()
        };

        let file = File::open(path).chain_err(|| "unable to open file")?;

        let reader = BufReader::new(file);

        for line in reader.lines() {
            let line = line.chain_err(|| "unable to read line")?;

            if line.is_empty() {
                continue;
            }

            let mut r_split = line.rsplitn(2, ';');

            if r_split.clone().count() > 1 {
                r_split.next();
            }

            if let Some(first_half) = r_split.next() {
                let first_half = first_half.trim();

                let first_half: String = if LABELED_MNEMONIC_RE.is_match(first_half) {
                    let captures = if let Some(captures) = LABELED_MNEMONIC_RE
                           .captures_iter(first_half)
                           .next() {
                        captures
                    } else {
                        bail!("no label capture found")
                    };

                    let label = captures[1].trim();

                    println!(
                        "Label {:?} found at address {}",
                        label,
                        self.mnemonics.len()
                    );

                    self.add_label(label.to_owned());

                    captures[2].trim().to_owned()
                } else if LABEL_RE.is_match(first_half) {
                    let captures =
                        if let Some(captures) = LABEL_RE.captures_iter(first_half).next() {
                            captures
                        } else {
                            bail!("no label capture found")
                        };

                    let label = captures[1].trim();

                    println!(
                        "Label {:?} found at address {}",
                        label,
                        self.mnemonics.len()
                    );

                    self.add_label(label.to_owned());
                    continue;
                } else if INCLUDE_RE.is_match(first_half) {
                    let captures =
                        if let Some(captures) = INCLUDE_RE.captures_iter(first_half).next() {
                            captures
                        } else {
                            bail!("no include capture found")
                        };

                    self.compile_mnemonics(captures[1].trim().to_owned() + ".basm")?;

                    continue;
                } else {
                    first_half.to_owned()
                };

                let mut first_half_split = first_half.splitn(2, ' ');

                if let Some(opcode) = first_half_split.next() {
                    let opcode = opcode.trim().to_lowercase();

                    let args: Vec<String> = if let Some(args) = first_half_split.next() {
                        args.split(',').map(|arg| arg.trim().to_owned()).collect()
                    } else {
                        Vec::new()
                    };

                    println!("Adding mnemonic {:?} with args: {:?}", opcode, args);

                    self.mnemonics.push(text_to_mnemonic(opcode, args)?);
                } else {
                    bail!("opcode expected. Found {:?}", first_half_split);
                }
            } else {
                bail!("instruction expected. Found {:?}", line.to_string());
            }
        }

        Ok(())
    }

    fn compile_mnemonic(&mut self, mnemonic: Mnemonic) -> Result<()> {

        println!("Compiling mnemonic {:?}", mnemonic);

        match mnemonic {
            Mnemonic::Add(dest, src) => self.builder.add(dest, src),
            Mnemonic::Sub(dest, src) => self.builder.sub(dest, src),
            Mnemonic::Div(dest, src) => self.builder.div(dest, src),
            Mnemonic::Mul(dest, src) => self.builder.mul(dest, src),
            Mnemonic::Rem(dest, src) => self.builder.rem(dest, src),

            Mnemonic::Cmp(target_a, target_b) => self.builder.cmp(target_a, target_b),
            Mnemonic::Jmp(label) => {
                let addr = self.lookup(&label)?;
                self.builder.jmp(addr);
            }
            Mnemonic::JmpLt(label) => {
                let addr = self.lookup(&label)?;
                self.builder.jmp_lt(addr);
            }
            Mnemonic::JmpGt(label) => {
                let addr = self.lookup(&label)?;
                self.builder.jmp_gt(addr);
            }
            Mnemonic::JmpEq(label) => {
                let addr = self.lookup(&label)?;
                self.builder.jmp_eq(addr);
            }
            Mnemonic::JmpLtEq(label) => {
                let addr = self.lookup(&label)?;
                self.builder.jmp_lt_eq(addr);
            }
            Mnemonic::JmpGtEq(label) => {
                let addr = self.lookup(&label)?;
                self.builder.jmp_gt_eq(addr);
            }

            Mnemonic::Cast(target, type_t) => self.builder.cast(target, type_t),

            Mnemonic::Push(target, value) => self.builder.push(target, value),
            Mnemonic::Mov(dest, src) => self.builder.mov(dest, src),
            Mnemonic::Swp(target_a, target_b) => self.builder.swp(target_a, target_b),
            Mnemonic::Dup(target) => self.builder.dup(target),

            Mnemonic::Call(label) => {
                let addr = self.lookup(&label)?;
                self.builder.call(addr);
            }
            Mnemonic::Ret => self.builder.ret(),

            Mnemonic::Halt => self.builder.halt(),
            Mnemonic::Pause => self.builder.pause(),
            Mnemonic::Nop => self.builder.nop(),
            Mnemonic::Int(int) => self.builder.int(int),
        }

        Ok(())
    }

    fn lookup(&mut self, input: &String) -> Result<Address> {
        if let Some(addr) = self.label_addr_map.get(input) {
            Ok(*addr)
        } else {
            bail!("label {:?} not found", input);
        }
    }

    pub fn compile(&mut self, file_name: String) -> Result<ImageData> {
        self.compile_mnemonics(file_name)?;

        for mnemonic in self.mnemonics.clone() {
            self.compile_mnemonic(mnemonic)?;
        }

        Ok(self.builder.clone().gen())
    }
}

fn text_to_mnemonic(opcode: String, args: Vec<String>) -> Result<Mnemonic> {
    match opcode.as_str() {
        "add" => Ok(Mnemonic::Add(args[0].parse()?, args[1].parse()?)),
        "sub" => Ok(Mnemonic::Sub(args[0].parse()?, args[1].parse()?)),
        "div" => Ok(Mnemonic::Div(args[0].parse()?, args[1].parse()?)),
        "mul" => Ok(Mnemonic::Mul(args[0].parse()?, args[1].parse()?)),
        "rem" => Ok(Mnemonic::Rem(args[0].parse()?, args[1].parse()?)),

        "cmp" => Ok(Mnemonic::Cmp(args[0].parse()?, args[1].parse()?)),
        "jmp" => Ok(Mnemonic::Jmp(args[0].parse()?)),
        "jmplt" => Ok(Mnemonic::JmpLt(args[0].parse()?)),
        "jmpgt" => Ok(Mnemonic::JmpGt(args[0].parse()?)),
        "jmpeq" => Ok(Mnemonic::JmpEq(args[0].parse()?)),
        "jmplteq" => Ok(Mnemonic::JmpLtEq(args[0].parse()?)),
        "jmpgteq" => Ok(Mnemonic::JmpGtEq(args[0].parse()?)),

        "cast" => Ok(Mnemonic::Cast(args[0].parse()?, args[1].parse()?)),
        "push" => Ok(Mnemonic::Push(args[0].parse()?, args[1].parse()?)),
        "mov" => Ok(Mnemonic::Mov(args[0].parse()?, args[1].parse()?)),
        "swp" => Ok(Mnemonic::Swp(args[0].parse()?, args[1].parse()?)),
        "dup" => Ok(Mnemonic::Dup(args[0].parse()?)),

        "call" => Ok(Mnemonic::Call(args[0].parse()?)),
        "ret" => Ok(Mnemonic::Ret),

        "halt" => Ok(Mnemonic::Halt),
        "pause" => Ok(Mnemonic::Pause),
        "nop" => Ok(Mnemonic::Nop),
        "int" => Ok(Mnemonic::Int(args[0].parse()?)),
        _ => bail!("unkwnown opcode {:?}", opcode),
    }
}

pub fn compile(file_name: String) -> Result<ImageData> {
    BASMCompiler::default().compile(file_name)
}

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

    #[test]
    fn labeled_mnemonic_regex() {
        let input = ".start mov $st, $vi(19)";

        if !LABELED_MNEMONIC_RE.is_match(input) {
            panic!("input doesn't match a labeled mnemonic");
        } else {
            let captures = LABELED_MNEMONIC_RE.captures_iter(input).next().unwrap();

            assert_eq!(captures[1].trim(), "start");
            assert_eq!(captures[2].trim(), "mov $st, $vi(19)");
        }
    }

    #[test]
    fn label_regex() {
        let input = ".start";

        if !LABEL_RE.is_match(input) {
            panic!("input doesn't match a label");
        } else {
            let captures = LABEL_RE.captures_iter(input).next().unwrap();

            assert_eq!(captures[1].trim(), "start");
        }
    }

    #[test]
    fn include_regex() {
        let input = "include! std";

        if !INCLUDE_RE.is_match(input) {
            panic!("input doesn't match an include statement");
        } else {
            let captures = INCLUDE_RE.captures_iter(input).next().unwrap();

            assert_eq!(captures[1].trim(), "std");
        }
    }
}
<|endoftext|>