<commit_before>use super::{BeaconChain, ClientDB, DBError, SlotClock};
use bls::{PublicKey, Signature};
use hashing::hash;
use log::debug;
use slot_clock::{SystemTimeSlotClockError, TestingSlotClockError};
use ssz::{ssz_encode, TreeHash};
use types::{
    beacon_state::{AttestationValidationError, CommitteesError, SlotProcessingError},
    readers::BeaconBlockReader,
    BeaconBlock, BeaconState, Exit, Fork, Hash256, PendingAttestation,
};

// TODO: define elsehwere.
const DOMAIN_PROPOSAL: u64 = 2;
const DOMAIN_EXIT: u64 = 3;
const DOMAIN_RANDAO: u64 = 4;

macro_rules! ensure {
    ($condition: expr, $result: expr) => {
        if !$condition {
            return Err($result);
        }
    };
}

#[derive(Debug, PartialEq)]
pub enum Error {
    DBError(String),
    StateAlreadyTransitioned,
    NotImplemented,
    PresentSlotIsNone,
    UnableToDecodeBlock,
    MissingParentState(Hash256),
    InvalidParentState(Hash256),
    MissingBeaconBlock(Hash256),
    InvalidBeaconBlock(Hash256),
    MissingParentBlock(Hash256),
    NoBlockProducer,
    StateSlotMismatch,
    BadBlockSignature,
    BadRandaoSignature,
    MaxProposerSlashingsExceeded,
    BadProposerSlashing,
    MaxAttestationsExceeded,
    InvalidAttestation(AttestationValidationError),
    NoBlockRoot,
    MaxDepositsExceeded,
    MaxExitsExceeded,
    BadExit,
    BadCustodyReseeds,
    BadCustodyChallenges,
    BadCustodyResponses,
    SlotClockError(SystemTimeSlotClockError),
    CommitteesError(CommitteesError),
    SlotProcessingError(SlotProcessingError),
}

impl<T, U> BeaconChain<T, U>
where
    T: ClientDB,
    U: SlotClock,
{
    pub fn state_transition(
        &self,
        state: BeaconState,
        block: &BeaconBlock,
    ) -> Result<BeaconState, Error> {
        self.internal_state_transition(state, block, true)
    }

    pub fn state_transition_without_verifying_block_signature(
        &self,
        state: BeaconState,
        block: &BeaconBlock,
    ) -> Result<BeaconState, Error> {
        self.internal_state_transition(state, block, false)
    }

    fn internal_state_transition(
        &self,
        mut state: BeaconState,
        block: &BeaconBlock,
        verify_block_signature: bool,
    ) -> Result<BeaconState, Error> {
        ensure!(state.slot < block.slot, Error::StateAlreadyTransitioned);

        debug!(
            "Starting state transition from slot {} to {}...",
            state.slot, block.slot
        );

        for _ in state.slot..block.slot {
            state.per_slot_processing(block.parent_root.clone(), &self.spec)?;
        }

        /*
         * Slot
         */

        ensure!(block.slot() == state.slot, Error::StateSlotMismatch);

        /*
         * Proposer Signature
         */

        let block_proposer_index = state
            .get_beacon_proposer_index(block.slot, &self.spec)
            .map_err(|_| Error::NoBlockProducer)?;
        let block_proposer = &state.validator_registry[block_proposer_index];

        if verify_block_signature {
            ensure!(
                bls_verify(
                    &block_proposer.pubkey,
                    &block.proposal_root(&self.spec)[..],
                    &block.signature,
                    get_domain(&state.fork_data, state.slot, DOMAIN_PROPOSAL)
                ),
                Error::BadBlockSignature
            );
        }

        debug!("Block signature is valid.");

        /*
         * RANDAO
         */

        ensure!(
            bls_verify(
                &block_proposer.pubkey,
                &ssz_encode(&block_proposer.proposer_slots),
                &block.randao_reveal,
                get_domain(&state.fork_data, state.slot, DOMAIN_RANDAO)
            ),
            Error::BadRandaoSignature
        );

        debug!("RANDAO signature is valid.");

        // TODO: check this is correct.
        let new_mix = {
            let mut mix = state.latest_randao_mixes
                [(state.slot % self.spec.latest_randao_mixes_length) as usize]
                .to_vec();
            mix.append(&mut ssz_encode(&block.randao_reveal));
            Hash256::from(&hash(&mix)[..])
        };

        state.latest_randao_mixes[(state.slot % self.spec.latest_randao_mixes_length) as usize] =
            new_mix;

        /*
         * Eth1 data
         */

        // TODO: Eth1 data stuff.

        /*
         * OPERATIONS
         */

        /*
         * Proposer slashings
         */

        ensure!(
            block.body.proposer_slashings.len() as u64 <= self.spec.max_proposer_slashings,
            Error::MaxProposerSlashingsExceeded
        );
        for proposer_slashing in &block.body.proposer_slashings {
            let proposer = state
                .validator_registry
                .get(proposer_slashing.proposer_index as usize)
                .ok_or(Error::BadProposerSlashing)?;
            ensure!(
                proposer_slashing.proposal_data_1.slot == proposer_slashing.proposal_data_2.slot,
                Error::BadProposerSlashing
            );
            ensure!(
                proposer_slashing.proposal_data_1.shard == proposer_slashing.proposal_data_2.shard,
                Error::BadProposerSlashing
            );
            ensure!(
                proposer_slashing.proposal_data_1.block_root
                    != proposer_slashing.proposal_data_2.block_root,
                Error::BadProposerSlashing
            );
            ensure!(
                proposer.penalized_slot > state.slot,
                Error::BadProposerSlashing
            );
            ensure!(
                bls_verify(
                    &proposer.pubkey,
                    &proposer_slashing.proposal_data_1.hash_tree_root(),
                    &proposer_slashing.proposal_signature_1,
                    get_domain(
                        &state.fork_data,
                        proposer_slashing.proposal_data_1.slot,
                        DOMAIN_PROPOSAL
                    )
                ),
                Error::BadProposerSlashing
            );
            ensure!(
                bls_verify(
                    &proposer.pubkey,
                    &proposer_slashing.proposal_data_2.hash_tree_root(),
                    &proposer_slashing.proposal_signature_2,
                    get_domain(
                        &state.fork_data,
                        proposer_slashing.proposal_data_2.slot,
                        DOMAIN_PROPOSAL
                    )
                ),
                Error::BadProposerSlashing
            );
            penalize_validator(&state, proposer_slashing.proposer_index as usize);
        }

        /*
         * Attestations
         */
        ensure!(
            block.body.attestations.len() as u64 <= self.spec.max_attestations,
            Error::MaxAttestationsExceeded
        );

        for attestation in &block.body.attestations {
            state.validate_attestation(attestation, &self.spec)?;

            let pending_attestation = PendingAttestation {
                data: attestation.data.clone(),
                aggregation_bitfield: attestation.aggregation_bitfield.clone(),
                custody_bitfield: attestation.custody_bitfield.clone(),
                slot_included: state.slot,
            };
            state.latest_attestations.push(pending_attestation);
        }

        debug!(
            "{} attestations verified & processed.",
            block.body.attestations.len()
        );

        /*
         * Deposits
         */
        ensure!(
            block.body.deposits.len() as u64 <= self.spec.max_deposits,
            Error::MaxDepositsExceeded
        );

        // TODO: process deposits.

        /*
         * Exits
         */

        ensure!(
            block.body.exits.len() as u64 <= self.spec.max_exits,
            Error::MaxExitsExceeded
        );

        for exit in &block.body.exits {
            let validator = state
                .validator_registry
                .get(exit.validator_index as usize)
                .ok_or(Error::BadExit)?;
            ensure!(
                validator.exit_slot > state.slot + self.spec.entry_exit_delay,
                Error::BadExit
            );
            ensure!(state.slot >= exit.slot, Error::BadExit);
            let exit_message = {
                let exit_struct = Exit {
                    slot: exit.slot,
                    validator_index: exit.validator_index,
                    signature: self.spec.empty_signature.clone(),
                };
                exit_struct.hash_tree_root()
            };
            ensure!(
                bls_verify(
                    &validator.pubkey,
                    &exit_message,
                    &exit.signature,
                    get_domain(&state.fork_data, exit.slot, DOMAIN_EXIT)
                ),
                Error::BadProposerSlashing
            );
            initiate_validator_exit(&state, exit.validator_index);
        }

        /*
         * Custody
         */
        ensure!(
            block.body.custody_reseeds.is_empty(),
            Error::BadCustodyReseeds
        );
        ensure!(
            block.body.custody_challenges.is_empty(),
            Error::BadCustodyChallenges
        );
        ensure!(
            block.body.custody_responses.is_empty(),
            Error::BadCustodyResponses
        );

        debug!("State transition complete.");

        Ok(state)
    }
}

fn initiate_validator_exit(_state: &BeaconState, _index: u32) {
    // TODO: stubbed out.
}

fn penalize_validator(_state: &BeaconState, _proposer_index: usize) {
    // TODO: stubbed out.
}

fn get_domain(_fork: &Fork, _slot: u64, _domain_type: u64) -> u64 {
    // TODO: stubbed out.
    0
}

fn bls_verify(pubkey: &PublicKey, message: &[u8], signature: &Signature, _domain: u64) -> bool {
    // TODO: add domain
    signature.verify(message, pubkey)
}

impl From<DBError> for Error {
    fn from(e: DBError) -> Error {
        Error::DBError(e.message)
    }
}

impl From<TestingSlotClockError> for Error {
    fn from(_: TestingSlotClockError) -> Error {
        unreachable!(); // Testing clock never throws an error.
    }
}

impl From<SystemTimeSlotClockError> for Error {
    fn from(e: SystemTimeSlotClockError) -> Error {
        Error::SlotClockError(e)
    }
}

impl From<AttestationValidationError> for Error {
    fn from(e: AttestationValidationError) -> Error {
        Error::InvalidAttestation(e)
    }
}

impl From<CommitteesError> for Error {
    fn from(e: CommitteesError) -> Error {
        Error::CommitteesError(e)
    }
}

impl From<SlotProcessingError> for Error {
    fn from(e: SlotProcessingError) -> Error {
        Error::SlotProcessingError(e)
    }
}
<commit_msg>Tidy out some verbose debugs<commit_after>use super::{BeaconChain, ClientDB, DBError, SlotClock};
use bls::{PublicKey, Signature};
use hashing::hash;
use log::debug;
use slot_clock::{SystemTimeSlotClockError, TestingSlotClockError};
use ssz::{ssz_encode, TreeHash};
use types::{
    beacon_state::{AttestationValidationError, CommitteesError, SlotProcessingError},
    readers::BeaconBlockReader,
    BeaconBlock, BeaconState, Exit, Fork, Hash256, PendingAttestation,
};

// TODO: define elsehwere.
const DOMAIN_PROPOSAL: u64 = 2;
const DOMAIN_EXIT: u64 = 3;
const DOMAIN_RANDAO: u64 = 4;

macro_rules! ensure {
    ($condition: expr, $result: expr) => {
        if !$condition {
            return Err($result);
        }
    };
}

#[derive(Debug, PartialEq)]
pub enum Error {
    DBError(String),
    StateAlreadyTransitioned,
    NotImplemented,
    PresentSlotIsNone,
    UnableToDecodeBlock,
    MissingParentState(Hash256),
    InvalidParentState(Hash256),
    MissingBeaconBlock(Hash256),
    InvalidBeaconBlock(Hash256),
    MissingParentBlock(Hash256),
    NoBlockProducer,
    StateSlotMismatch,
    BadBlockSignature,
    BadRandaoSignature,
    MaxProposerSlashingsExceeded,
    BadProposerSlashing,
    MaxAttestationsExceeded,
    InvalidAttestation(AttestationValidationError),
    NoBlockRoot,
    MaxDepositsExceeded,
    MaxExitsExceeded,
    BadExit,
    BadCustodyReseeds,
    BadCustodyChallenges,
    BadCustodyResponses,
    SlotClockError(SystemTimeSlotClockError),
    CommitteesError(CommitteesError),
    SlotProcessingError(SlotProcessingError),
}

impl<T, U> BeaconChain<T, U>
where
    T: ClientDB,
    U: SlotClock,
{
    pub fn state_transition(
        &self,
        state: BeaconState,
        block: &BeaconBlock,
    ) -> Result<BeaconState, Error> {
        self.internal_state_transition(state, block, true)
    }

    pub fn state_transition_without_verifying_block_signature(
        &self,
        state: BeaconState,
        block: &BeaconBlock,
    ) -> Result<BeaconState, Error> {
        self.internal_state_transition(state, block, false)
    }

    fn internal_state_transition(
        &self,
        mut state: BeaconState,
        block: &BeaconBlock,
        verify_block_signature: bool,
    ) -> Result<BeaconState, Error> {
        ensure!(state.slot < block.slot, Error::StateAlreadyTransitioned);

        debug!(
            "Starting state transition from slot {} to {}...",
            state.slot, block.slot
        );

        for _ in state.slot..block.slot {
            state.per_slot_processing(block.parent_root.clone(), &self.spec)?;
        }

        /*
         * Slot
         */

        ensure!(block.slot() == state.slot, Error::StateSlotMismatch);

        /*
         * Proposer Signature
         */

        let block_proposer_index = state
            .get_beacon_proposer_index(block.slot, &self.spec)
            .map_err(|_| Error::NoBlockProducer)?;
        let block_proposer = &state.validator_registry[block_proposer_index];

        if verify_block_signature {
            ensure!(
                bls_verify(
                    &block_proposer.pubkey,
                    &block.proposal_root(&self.spec)[..],
                    &block.signature,
                    get_domain(&state.fork_data, state.slot, DOMAIN_PROPOSAL)
                ),
                Error::BadBlockSignature
            );
        }

        /*
         * RANDAO
         */

        ensure!(
            bls_verify(
                &block_proposer.pubkey,
                &ssz_encode(&block_proposer.proposer_slots),
                &block.randao_reveal,
                get_domain(&state.fork_data, state.slot, DOMAIN_RANDAO)
            ),
            Error::BadRandaoSignature
        );

        // TODO: check this is correct.
        let new_mix = {
            let mut mix = state.latest_randao_mixes
                [(state.slot % self.spec.latest_randao_mixes_length) as usize]
                .to_vec();
            mix.append(&mut ssz_encode(&block.randao_reveal));
            Hash256::from(&hash(&mix)[..])
        };

        state.latest_randao_mixes[(state.slot % self.spec.latest_randao_mixes_length) as usize] =
            new_mix;

        /*
         * Eth1 data
         */

        // TODO: Eth1 data stuff.

        /*
         * OPERATIONS
         */

        /*
         * Proposer slashings
         */

        ensure!(
            block.body.proposer_slashings.len() as u64 <= self.spec.max_proposer_slashings,
            Error::MaxProposerSlashingsExceeded
        );
        for proposer_slashing in &block.body.proposer_slashings {
            let proposer = state
                .validator_registry
                .get(proposer_slashing.proposer_index as usize)
                .ok_or(Error::BadProposerSlashing)?;
            ensure!(
                proposer_slashing.proposal_data_1.slot == proposer_slashing.proposal_data_2.slot,
                Error::BadProposerSlashing
            );
            ensure!(
                proposer_slashing.proposal_data_1.shard == proposer_slashing.proposal_data_2.shard,
                Error::BadProposerSlashing
            );
            ensure!(
                proposer_slashing.proposal_data_1.block_root
                    != proposer_slashing.proposal_data_2.block_root,
                Error::BadProposerSlashing
            );
            ensure!(
                proposer.penalized_slot > state.slot,
                Error::BadProposerSlashing
            );
            ensure!(
                bls_verify(
                    &proposer.pubkey,
                    &proposer_slashing.proposal_data_1.hash_tree_root(),
                    &proposer_slashing.proposal_signature_1,
                    get_domain(
                        &state.fork_data,
                        proposer_slashing.proposal_data_1.slot,
                        DOMAIN_PROPOSAL
                    )
                ),
                Error::BadProposerSlashing
            );
            ensure!(
                bls_verify(
                    &proposer.pubkey,
                    &proposer_slashing.proposal_data_2.hash_tree_root(),
                    &proposer_slashing.proposal_signature_2,
                    get_domain(
                        &state.fork_data,
                        proposer_slashing.proposal_data_2.slot,
                        DOMAIN_PROPOSAL
                    )
                ),
                Error::BadProposerSlashing
            );
            penalize_validator(&state, proposer_slashing.proposer_index as usize);
        }

        /*
         * Attestations
         */
        ensure!(
            block.body.attestations.len() as u64 <= self.spec.max_attestations,
            Error::MaxAttestationsExceeded
        );

        for attestation in &block.body.attestations {
            state.validate_attestation(attestation, &self.spec)?;

            let pending_attestation = PendingAttestation {
                data: attestation.data.clone(),
                aggregation_bitfield: attestation.aggregation_bitfield.clone(),
                custody_bitfield: attestation.custody_bitfield.clone(),
                slot_included: state.slot,
            };
            state.latest_attestations.push(pending_attestation);
        }

        debug!(
            "{} attestations verified & processed.",
            block.body.attestations.len()
        );

        /*
         * Deposits
         */
        ensure!(
            block.body.deposits.len() as u64 <= self.spec.max_deposits,
            Error::MaxDepositsExceeded
        );

        // TODO: process deposits.

        /*
         * Exits
         */

        ensure!(
            block.body.exits.len() as u64 <= self.spec.max_exits,
            Error::MaxExitsExceeded
        );

        for exit in &block.body.exits {
            let validator = state
                .validator_registry
                .get(exit.validator_index as usize)
                .ok_or(Error::BadExit)?;
            ensure!(
                validator.exit_slot > state.slot + self.spec.entry_exit_delay,
                Error::BadExit
            );
            ensure!(state.slot >= exit.slot, Error::BadExit);
            let exit_message = {
                let exit_struct = Exit {
                    slot: exit.slot,
                    validator_index: exit.validator_index,
                    signature: self.spec.empty_signature.clone(),
                };
                exit_struct.hash_tree_root()
            };
            ensure!(
                bls_verify(
                    &validator.pubkey,
                    &exit_message,
                    &exit.signature,
                    get_domain(&state.fork_data, exit.slot, DOMAIN_EXIT)
                ),
                Error::BadProposerSlashing
            );
            initiate_validator_exit(&state, exit.validator_index);
        }

        /*
         * Custody
         */
        ensure!(
            block.body.custody_reseeds.is_empty(),
            Error::BadCustodyReseeds
        );
        ensure!(
            block.body.custody_challenges.is_empty(),
            Error::BadCustodyChallenges
        );
        ensure!(
            block.body.custody_responses.is_empty(),
            Error::BadCustodyResponses
        );

        debug!("State transition complete.");

        Ok(state)
    }
}

fn initiate_validator_exit(_state: &BeaconState, _index: u32) {
    // TODO: stubbed out.
}

fn penalize_validator(_state: &BeaconState, _proposer_index: usize) {
    // TODO: stubbed out.
}

fn get_domain(_fork: &Fork, _slot: u64, _domain_type: u64) -> u64 {
    // TODO: stubbed out.
    0
}

fn bls_verify(pubkey: &PublicKey, message: &[u8], signature: &Signature, _domain: u64) -> bool {
    // TODO: add domain
    signature.verify(message, pubkey)
}

impl From<DBError> for Error {
    fn from(e: DBError) -> Error {
        Error::DBError(e.message)
    }
}

impl From<TestingSlotClockError> for Error {
    fn from(_: TestingSlotClockError) -> Error {
        unreachable!(); // Testing clock never throws an error.
    }
}

impl From<SystemTimeSlotClockError> for Error {
    fn from(e: SystemTimeSlotClockError) -> Error {
        Error::SlotClockError(e)
    }
}

impl From<AttestationValidationError> for Error {
    fn from(e: AttestationValidationError) -> Error {
        Error::InvalidAttestation(e)
    }
}

impl From<CommitteesError> for Error {
    fn from(e: CommitteesError) -> Error {
        Error::CommitteesError(e)
    }
}

impl From<SlotProcessingError> for Error {
    fn from(e: SlotProcessingError) -> Error {
        Error::SlotProcessingError(e)
    }
}
<|endoftext|>


<commit_before>use rustc::hir::def_id::DefId;
use rustc::hir;
use rustc::mir::*;
use rustc::ty::{self, Predicate, TyCtxt};
use rustc_target::spec::abi;
use std::borrow::Cow;
use syntax_pos::Span;

type McfResult = Result<(), (Span, Cow<'static, str>)>;

pub fn is_min_const_fn(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    def_id: DefId,
    mir: &'a Mir<'tcx>,
) -> McfResult {
    let mut current = def_id;
    loop {
        let predicates = tcx.predicates_of(current);
        for (predicate, _) in &predicates.predicates {
            match predicate {
                | Predicate::RegionOutlives(_)
                | Predicate::TypeOutlives(_)
                | Predicate::WellFormed(_)
                | Predicate::ConstEvaluatable(..) => continue,
                | Predicate::ObjectSafe(_) => {
                    bug!("object safe predicate on function: {:#?}", predicate)
                }
                Predicate::ClosureKind(..) => {
                    bug!("closure kind predicate on function: {:#?}", predicate)
                }
                Predicate::Subtype(_) => bug!("subtype predicate on function: {:#?}", predicate),
                Predicate::Projection(_) => {
                    let span = tcx.def_span(current);
                    // we'll hit a `Predicate::Trait` later which will report an error
                    tcx.sess
                        .delay_span_bug(span, "projection without trait bound");
                    continue;
                }
                Predicate::Trait(pred) => {
                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                        continue;
                    }
                    match pred.skip_binder().self_ty().sty {
                        ty::Param(ref p) => {
                            let generics = tcx.generics_of(current);
                            let def = generics.type_param(p, tcx);
                            let span = tcx.def_span(def.def_id);
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        }
                        // other kinds of bounds are either tautologies
                        // or cause errors in other passes
                        _ => continue,
                    }
                }
            }
        }
        match predicates.parent {
            Some(parent) => current = parent,
            None => break,
        }
    }

    for local in mir.vars_iter() {
        return Err((
            mir.local_decls[local].source_info.span,
            "local variables in const fn are unstable".into(),
        ));
    }
    for local in &mir.local_decls {
        check_ty(tcx, local.ty, local.source_info.span)?;
    }
    // impl trait is gone in MIR, so check the return type manually
    check_ty(
        tcx,
        tcx.fn_sig(def_id).output().skip_binder(),
        mir.local_decls.iter().next().unwrap().source_info.span,
    )?;

    for bb in mir.basic_blocks() {
        check_terminator(tcx, mir, bb.terminator())?;
        for stmt in &bb.statements {
            check_statement(tcx, mir, stmt)?;
        }
    }
    Ok(())
}

fn check_ty(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    ty: ty::Ty<'tcx>,
    span: Span,
) -> McfResult {
    for ty in ty.walk() {
        match ty.sty {
            ty::Ref(_, _, hir::Mutability::MutMutable) => return Err((
                span,
                "mutable references in const fn are unstable".into(),
            )),
            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
            ty::FnPtr(..) => {
                return Err((span, "function pointers in const fn are unstable".into()))
            }
            ty::Dynamic(preds, _) => {
                for pred in preds.iter() {
                    match pred.skip_binder() {
                        | ty::ExistentialPredicate::AutoTrait(_)
                        | ty::ExistentialPredicate::Projection(_) => {
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ))
                        }
                        ty::ExistentialPredicate::Trait(trait_ref) => {
                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
                                return Err((
                                    span,
                                    "trait bounds other than `Sized` \
                                     on const fn parameters are unstable"
                                        .into(),
                                ));
                            }
                        }
                    }
                }
            }
            _ => {}
        }
    }
    Ok(())
}

fn check_rvalue(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    rvalue: &Rvalue<'tcx>,
    span: Span,
) -> McfResult {
    match rvalue {
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
            check_operand(tcx, mir, operand, span)
        }
        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) => {
            check_place(tcx, mir, place, span, PlaceMode::Read)
        }
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(mir, tcx)).expect("bad input type for cast");
            let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
            match (cast_in, cast_out) {
                (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => Err((
                    span,
                    "casting pointers to ints is unstable in const fn".into(),
                )),
                (CastTy::RPtr(_), CastTy::Float) => bug!(),
                (CastTy::RPtr(_), CastTy::Int(_)) => bug!(),
                (CastTy::Ptr(_), CastTy::RPtr(_)) => bug!(),
                _ => check_operand(tcx, mir, operand, span),
            }
        }
        Rvalue::Cast(CastKind::UnsafeFnPointer, _, _) |
        Rvalue::Cast(CastKind::ClosureFnPointer, _, _) |
        Rvalue::Cast(CastKind::ReifyFnPointer, _, _) => Err((
            span,
            "function pointer casts are not allowed in const fn".into(),
        )),
        Rvalue::Cast(CastKind::Unsize, _, _) => Err((
            span,
            "unsizing casts are not allowed in const fn".into(),
        )),
        // binops are fine on integers
        Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
            check_operand(tcx, mir, lhs, span)?;
            check_operand(tcx, mir, rhs, span)?;
            let ty = lhs.ty(mir, tcx);
            if ty.is_integral() || ty.is_bool() || ty.is_char() {
                Ok(())
            } else {
                Err((
                    span,
                    "only int, `bool` and `char` operations are stable in const fn".into(),
                ))
            }
        }
        Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
        Rvalue::NullaryOp(NullOp::Box, _) => Err((
            span,
            "heap allocations are not allowed in const fn".into(),
        )),
        Rvalue::UnaryOp(_, operand) => {
            let ty = operand.ty(mir, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(tcx, mir, operand, span)
            } else {
                Err((
                    span,
                    "only int and `bool` operations are stable in const fn".into(),
                ))
            }
        }
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(tcx, mir, operand, span)?;
            }
            Ok(())
        }
    }
}

enum PlaceMode {
    Assign,
    Read,
}

fn check_statement(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    statement: &Statement<'tcx>,
) -> McfResult {
    let span = statement.source_info.span;
    match &statement.kind {
        StatementKind::Assign(place, rval) => {
            check_place(tcx, mir, place, span, PlaceMode::Assign)?;
            check_rvalue(tcx, mir, rval, span)
        }

        StatementKind::FakeRead(_, place) => check_place(tcx, mir, place, span, PlaceMode::Read),

        // just an assignment
        StatementKind::SetDiscriminant { .. } => Ok(()),

        | StatementKind::InlineAsm { .. } => {
            Err((span, "cannot use inline assembly in const fn".into()))
        }

        // These are all NOPs
        | StatementKind::StorageLive(_)
        | StatementKind::StorageDead(_)
        | StatementKind::Retag { .. }
        | StatementKind::AscribeUserType(..)
        | StatementKind::Nop => Ok(()),
    }
}

fn check_operand(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    operand: &Operand<'tcx>,
    span: Span,
) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => {
            check_place(tcx, mir, place, span, PlaceMode::Read)
        }
        Operand::Constant(_) => Ok(()),
    }
}

fn check_place(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    place: &Place<'tcx>,
    span: Span,
    mode: PlaceMode,
) -> McfResult {
    match place {
        Place::Local(l) => match mode {
            PlaceMode::Assign => match mir.local_kind(*l) {
                LocalKind::Temp | LocalKind::ReturnPointer => Ok(()),
                LocalKind::Arg | LocalKind::Var => {
                    Err((span, "assignments in const fn are unstable".into()))
                }
            },
            PlaceMode::Read => Ok(()),
        },
        // promoteds are always fine, they are essentially constants
        Place::Promoted(_) => Ok(()),
        Place::Static(_) => Err((span, "cannot access `static` items in const fn".into())),
        Place::Projection(proj) => {
            match proj.elem {
                | ProjectionElem::Deref | ProjectionElem::Field(..) | ProjectionElem::Index(_) => {
                    check_place(tcx, mir, &proj.base, span, mode)
                }
                // slice patterns are unstable
                | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
                    return Err((span, "slice patterns in const fn are unstable".into()))
                }
                | ProjectionElem::Downcast(..) => {
                    Err((span, "`match` or `if let` in `const fn` is unstable".into()))
                }
            }
        }
    }
}

fn check_terminator(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    terminator: &Terminator<'tcx>,
) -> McfResult {
    let span = terminator.source_info.span;
    match &terminator.kind {
        | TerminatorKind::Goto { .. }
        | TerminatorKind::Return
        | TerminatorKind::Resume => Ok(()),

        TerminatorKind::Drop { location, .. } => {
            check_place(tcx, mir, location, span, PlaceMode::Read)
        }
        TerminatorKind::DropAndReplace { location, value, .. } => {
            check_place(tcx, mir, location, span, PlaceMode::Read)?;
            check_operand(tcx, mir, value, span)
        },

        TerminatorKind::FalseEdges { .. } | TerminatorKind::SwitchInt { .. } => Err((
            span,
            "`if`, `match`, `&&` and `||` are not stable in const fn".into(),
        )),
        | TerminatorKind::Abort | TerminatorKind::Unreachable => {
            Err((span, "const fn with unreachable code is not stable".into()))
        }
        | TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
            Err((span, "const fn generators are unstable".into()))
        }

        TerminatorKind::Call {
            func,
            args,
            from_hir_call: _,
            destination: _,
            cleanup: _,
        } => {
            let fn_ty = func.ty(mir, tcx);
            if let ty::FnDef(def_id, _) = fn_ty.sty {

                // some intrinsics are waved through if called inside the
                // standard library. Users never need to call them directly
                match tcx.fn_sig(def_id).abi() {
                    abi::Abi::RustIntrinsic => if !is_intrinsic_whitelisted(tcx, def_id) {
                        return Err((
                            span,
                            "can only call a curated list of intrinsics in `min_const_fn`".into(),
                        ))
                    },
                    abi::Abi::Rust if tcx.is_min_const_fn(def_id) => {},
                    abi::Abi::Rust => return Err((
                        span,
                        "can only call other `min_const_fn` within a `min_const_fn`".into(),
                    )),
                    abi => return Err((
                        span,
                        format!(
                            "cannot call functions with `{}` abi in `min_const_fn`",
                            abi,
                        ).into(),
                    )),
                }

                check_operand(tcx, mir, func, span)?;

                for arg in args {
                    check_operand(tcx, mir, arg, span)?;
                }
                Ok(())
            } else {
                Err((span, "can only call other const fns within const fn".into()))
            }
        }

        TerminatorKind::Assert {
            cond,
            expected: _,
            msg: _,
            target: _,
            cleanup: _,
        } => check_operand(tcx, mir, cond, span),

        TerminatorKind::FalseUnwind { .. } => {
            Err((span, "loops are not allowed in const fn".into()))
        },
    }
}

/// Returns true if the `def_id` refers to an intrisic which we've whitelisted.
///
/// Adding more intrinsics requires sign-off from @rust-lang/lang.
fn is_intrinsic_whitelisted(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
    match &tcx.item_name(def_id).as_str()[..] {
        | "size_of"
        | "min_align_of"
        | "needs_drop"
        // Arithmetic:
        | "overflowing_add" // ~> .wrapping_add
        | "overflowing_sub" // ~> .wrapping_sub
        | "overflowing_mul" // ~> .wrapping_mul
        | "unchecked_shl" // ~> .wrapping_shl
        | "unchecked_shr" // ~> .wrapping_shr
        | "rotate_left" // ~> .rotate_left
        | "rotate_right" // ~> .rotate_right
        => true,
        _ => false,
    }
}
<commit_msg>qualify_min_const_fn: improve note.<commit_after>use rustc::hir::def_id::DefId;
use rustc::hir;
use rustc::mir::*;
use rustc::ty::{self, Predicate, TyCtxt};
use rustc_target::spec::abi;
use std::borrow::Cow;
use syntax_pos::Span;

type McfResult = Result<(), (Span, Cow<'static, str>)>;

pub fn is_min_const_fn(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    def_id: DefId,
    mir: &'a Mir<'tcx>,
) -> McfResult {
    let mut current = def_id;
    loop {
        let predicates = tcx.predicates_of(current);
        for (predicate, _) in &predicates.predicates {
            match predicate {
                | Predicate::RegionOutlives(_)
                | Predicate::TypeOutlives(_)
                | Predicate::WellFormed(_)
                | Predicate::ConstEvaluatable(..) => continue,
                | Predicate::ObjectSafe(_) => {
                    bug!("object safe predicate on function: {:#?}", predicate)
                }
                Predicate::ClosureKind(..) => {
                    bug!("closure kind predicate on function: {:#?}", predicate)
                }
                Predicate::Subtype(_) => bug!("subtype predicate on function: {:#?}", predicate),
                Predicate::Projection(_) => {
                    let span = tcx.def_span(current);
                    // we'll hit a `Predicate::Trait` later which will report an error
                    tcx.sess
                        .delay_span_bug(span, "projection without trait bound");
                    continue;
                }
                Predicate::Trait(pred) => {
                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                        continue;
                    }
                    match pred.skip_binder().self_ty().sty {
                        ty::Param(ref p) => {
                            let generics = tcx.generics_of(current);
                            let def = generics.type_param(p, tcx);
                            let span = tcx.def_span(def.def_id);
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        }
                        // other kinds of bounds are either tautologies
                        // or cause errors in other passes
                        _ => continue,
                    }
                }
            }
        }
        match predicates.parent {
            Some(parent) => current = parent,
            None => break,
        }
    }

    for local in mir.vars_iter() {
        return Err((
            mir.local_decls[local].source_info.span,
            "local variables in const fn are unstable".into(),
        ));
    }
    for local in &mir.local_decls {
        check_ty(tcx, local.ty, local.source_info.span)?;
    }
    // impl trait is gone in MIR, so check the return type manually
    check_ty(
        tcx,
        tcx.fn_sig(def_id).output().skip_binder(),
        mir.local_decls.iter().next().unwrap().source_info.span,
    )?;

    for bb in mir.basic_blocks() {
        check_terminator(tcx, mir, bb.terminator())?;
        for stmt in &bb.statements {
            check_statement(tcx, mir, stmt)?;
        }
    }
    Ok(())
}

fn check_ty(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    ty: ty::Ty<'tcx>,
    span: Span,
) -> McfResult {
    for ty in ty.walk() {
        match ty.sty {
            ty::Ref(_, _, hir::Mutability::MutMutable) => return Err((
                span,
                "mutable references in const fn are unstable".into(),
            )),
            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
            ty::FnPtr(..) => {
                return Err((span, "function pointers in const fn are unstable".into()))
            }
            ty::Dynamic(preds, _) => {
                for pred in preds.iter() {
                    match pred.skip_binder() {
                        | ty::ExistentialPredicate::AutoTrait(_)
                        | ty::ExistentialPredicate::Projection(_) => {
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ))
                        }
                        ty::ExistentialPredicate::Trait(trait_ref) => {
                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
                                return Err((
                                    span,
                                    "trait bounds other than `Sized` \
                                     on const fn parameters are unstable"
                                        .into(),
                                ));
                            }
                        }
                    }
                }
            }
            _ => {}
        }
    }
    Ok(())
}

fn check_rvalue(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    rvalue: &Rvalue<'tcx>,
    span: Span,
) -> McfResult {
    match rvalue {
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
            check_operand(tcx, mir, operand, span)
        }
        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) => {
            check_place(tcx, mir, place, span, PlaceMode::Read)
        }
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(mir, tcx)).expect("bad input type for cast");
            let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
            match (cast_in, cast_out) {
                (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => Err((
                    span,
                    "casting pointers to ints is unstable in const fn".into(),
                )),
                (CastTy::RPtr(_), CastTy::Float) => bug!(),
                (CastTy::RPtr(_), CastTy::Int(_)) => bug!(),
                (CastTy::Ptr(_), CastTy::RPtr(_)) => bug!(),
                _ => check_operand(tcx, mir, operand, span),
            }
        }
        Rvalue::Cast(CastKind::UnsafeFnPointer, _, _) |
        Rvalue::Cast(CastKind::ClosureFnPointer, _, _) |
        Rvalue::Cast(CastKind::ReifyFnPointer, _, _) => Err((
            span,
            "function pointer casts are not allowed in const fn".into(),
        )),
        Rvalue::Cast(CastKind::Unsize, _, _) => Err((
            span,
            "unsizing casts are not allowed in const fn".into(),
        )),
        // binops are fine on integers
        Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
            check_operand(tcx, mir, lhs, span)?;
            check_operand(tcx, mir, rhs, span)?;
            let ty = lhs.ty(mir, tcx);
            if ty.is_integral() || ty.is_bool() || ty.is_char() {
                Ok(())
            } else {
                Err((
                    span,
                    "only int, `bool` and `char` operations are stable in const fn".into(),
                ))
            }
        }
        Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
        Rvalue::NullaryOp(NullOp::Box, _) => Err((
            span,
            "heap allocations are not allowed in const fn".into(),
        )),
        Rvalue::UnaryOp(_, operand) => {
            let ty = operand.ty(mir, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(tcx, mir, operand, span)
            } else {
                Err((
                    span,
                    "only int and `bool` operations are stable in const fn".into(),
                ))
            }
        }
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(tcx, mir, operand, span)?;
            }
            Ok(())
        }
    }
}

enum PlaceMode {
    Assign,
    Read,
}

fn check_statement(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    statement: &Statement<'tcx>,
) -> McfResult {
    let span = statement.source_info.span;
    match &statement.kind {
        StatementKind::Assign(place, rval) => {
            check_place(tcx, mir, place, span, PlaceMode::Assign)?;
            check_rvalue(tcx, mir, rval, span)
        }

        StatementKind::FakeRead(_, place) => check_place(tcx, mir, place, span, PlaceMode::Read),

        // just an assignment
        StatementKind::SetDiscriminant { .. } => Ok(()),

        | StatementKind::InlineAsm { .. } => {
            Err((span, "cannot use inline assembly in const fn".into()))
        }

        // These are all NOPs
        | StatementKind::StorageLive(_)
        | StatementKind::StorageDead(_)
        | StatementKind::Retag { .. }
        | StatementKind::AscribeUserType(..)
        | StatementKind::Nop => Ok(()),
    }
}

fn check_operand(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    operand: &Operand<'tcx>,
    span: Span,
) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => {
            check_place(tcx, mir, place, span, PlaceMode::Read)
        }
        Operand::Constant(_) => Ok(()),
    }
}

fn check_place(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    place: &Place<'tcx>,
    span: Span,
    mode: PlaceMode,
) -> McfResult {
    match place {
        Place::Local(l) => match mode {
            PlaceMode::Assign => match mir.local_kind(*l) {
                LocalKind::Temp | LocalKind::ReturnPointer => Ok(()),
                LocalKind::Arg | LocalKind::Var => {
                    Err((span, "assignments in const fn are unstable".into()))
                }
            },
            PlaceMode::Read => Ok(()),
        },
        // promoteds are always fine, they are essentially constants
        Place::Promoted(_) => Ok(()),
        Place::Static(_) => Err((span, "cannot access `static` items in const fn".into())),
        Place::Projection(proj) => {
            match proj.elem {
                | ProjectionElem::Deref | ProjectionElem::Field(..) | ProjectionElem::Index(_) => {
                    check_place(tcx, mir, &proj.base, span, mode)
                }
                // slice patterns are unstable
                | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
                    return Err((span, "slice patterns in const fn are unstable".into()))
                }
                | ProjectionElem::Downcast(..) => {
                    Err((span, "`match` or `if let` in `const fn` is unstable".into()))
                }
            }
        }
    }
}

fn check_terminator(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    terminator: &Terminator<'tcx>,
) -> McfResult {
    let span = terminator.source_info.span;
    match &terminator.kind {
        | TerminatorKind::Goto { .. }
        | TerminatorKind::Return
        | TerminatorKind::Resume => Ok(()),

        TerminatorKind::Drop { location, .. } => {
            check_place(tcx, mir, location, span, PlaceMode::Read)
        }
        TerminatorKind::DropAndReplace { location, value, .. } => {
            check_place(tcx, mir, location, span, PlaceMode::Read)?;
            check_operand(tcx, mir, value, span)
        },

        TerminatorKind::FalseEdges { .. } | TerminatorKind::SwitchInt { .. } => Err((
            span,
            "`if`, `match`, `&&` and `||` are not stable in const fn".into(),
        )),
        | TerminatorKind::Abort | TerminatorKind::Unreachable => {
            Err((span, "const fn with unreachable code is not stable".into()))
        }
        | TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
            Err((span, "const fn generators are unstable".into()))
        }

        TerminatorKind::Call {
            func,
            args,
            from_hir_call: _,
            destination: _,
            cleanup: _,
        } => {
            let fn_ty = func.ty(mir, tcx);
            if let ty::FnDef(def_id, _) = fn_ty.sty {

                // some intrinsics are waved through if called inside the
                // standard library. Users never need to call them directly
                match tcx.fn_sig(def_id).abi() {
                    abi::Abi::RustIntrinsic => if !is_intrinsic_whitelisted(tcx, def_id) {
                        return Err((
                            span,
                            "can only call a curated list of intrinsics in `min_const_fn`".into(),
                        ))
                    },
                    abi::Abi::Rust if tcx.is_min_const_fn(def_id) => {},
                    abi::Abi::Rust => return Err((
                        span,
                        "can only call other `min_const_fn` within a `min_const_fn`".into(),
                    )),
                    abi => return Err((
                        span,
                        format!(
                            "cannot call functions with `{}` abi in `min_const_fn`",
                            abi,
                        ).into(),
                    )),
                }

                check_operand(tcx, mir, func, span)?;

                for arg in args {
                    check_operand(tcx, mir, arg, span)?;
                }
                Ok(())
            } else {
                Err((span, "can only call other const fns within const fn".into()))
            }
        }

        TerminatorKind::Assert {
            cond,
            expected: _,
            msg: _,
            target: _,
            cleanup: _,
        } => check_operand(tcx, mir, cond, span),

        TerminatorKind::FalseUnwind { .. } => {
            Err((span, "loops are not allowed in const fn".into()))
        },
    }
}

/// Returns true if the `def_id` refers to an intrisic which we've whitelisted
/// for being called from stable `const fn`s (`min_const_fn`).
///
/// Adding more intrinsics requires sign-off from @rust-lang/lang.
fn is_intrinsic_whitelisted(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
    match &tcx.item_name(def_id).as_str()[..] {
        | "size_of"
        | "min_align_of"
        | "needs_drop"
        // Arithmetic:
        | "overflowing_add" // ~> .wrapping_add
        | "overflowing_sub" // ~> .wrapping_sub
        | "overflowing_mul" // ~> .wrapping_mul
        | "unchecked_shl" // ~> .wrapping_shl
        | "unchecked_shr" // ~> .wrapping_shr
        | "rotate_left" // ~> .rotate_left
        | "rotate_right" // ~> .rotate_right
        => true,
        _ => false,
    }
}
<|endoftext|>


<commit_before>#![feature(plugin_registrar)]
#![feature(rustc_private)]

//! basic syntax extension to calculate the factorial of 10 at compile time for the compile-time
//! calculation task (the task itself is in `src/compile_time_calculation.rs`)
extern crate syntax;
extern crate rustc_plugin;

use syntax::ast;
use syntax::codemap::Span;
use syntax::parse;
use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager};
use syntax::ext::build::AstBuilder;  // trait for expr_usize
use syntax::tokenstream::TokenTree;
use rustc_plugin::Registry;

fn exp_factorial(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult + 'static> {
    // extract the argument and ensure there is only one and it's a usize
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg().clone(), tts.to_vec());

    // Try to parse a literal (doesn't need to be a number)
    let literal = match parser.parse_lit() {
        Ok(l) => l,
        Err(_) => {
            // span_err shows a compile time error to the user
            cx.span_err(sp, "fatal error");
            return DummyResult::any(sp);
        }
    };

    // Take the number of the literal (if it is a number)
    let n: u64 = match literal.node {
        ast::LitKind::Int(x, _) => x,
        _ => {
            cx.span_err(sp, "Invalid literal (expected unsigned integer)");
            return DummyResult::any(sp);
        }
    };

    // calculating the factorial
    let result = (1..(n as usize) + 1).fold(1, |accum, elem| accum * elem);

    MacEager::expr(cx.expr_usize(sp, result))
}

/// here's where we register the macro with the name of `factorial` so that it can be invoked a
/// like every other macro with a ! at the end
#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("factorial", exp_factorial);
}
<commit_msg>update compiler plugin for nightly<commit_after>#![feature(plugin_registrar)]
#![feature(rustc_private)]

//! basic syntax extension to calculate the factorial of 10 at compile time for the compile-time
//! calculation task (the task itself is in `src/compile_time_calculation.rs`)
extern crate syntax;
extern crate rustc_plugin;

use syntax::ast;
use syntax::codemap::Span;
use syntax::parse;
use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager};
use syntax::ext::build::AstBuilder;  // trait for expr_usize
use syntax::tokenstream::TokenTree;
use rustc_plugin::Registry;

fn exp_factorial(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult + 'static> {
    // extract the argument and ensure there is only one and it's a usize
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), tts.to_vec());

    // Try to parse a literal (doesn't need to be a number)
    let literal = match parser.parse_lit() {
        Ok(l) => l,
        Err(_) => {
            // span_err shows a compile time error to the user
            cx.span_err(sp, "fatal error");
            return DummyResult::any(sp);
        }
    };

    // Take the number of the literal (if it is a number)
    let n: u64 = match literal.node {
        ast::LitKind::Int(x, _) => x,
        _ => {
            cx.span_err(sp, "Invalid literal (expected unsigned integer)");
            return DummyResult::any(sp);
        }
    };

    // calculating the factorial
    let result = (1..(n as usize) + 1).fold(1, |accum, elem| accum * elem);

    MacEager::expr(cx.expr_usize(sp, result))
}

/// here's where we register the macro with the name of `factorial` so that it can be invoked a
/// like every other macro with a ! at the end
#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("factorial", exp_factorial);
}
<|endoftext|>


<commit_before>use std::path::Path;
use std::collections::{VecDeque, BTreeMap};

use serde_json;
use handlebars::{Handlebars, HelperDef, RenderError, RenderContext, Helper};
use pulldown_cmark::{Parser, html, Event, Tag};

// Handlebars helper to construct TOC
#[derive(Clone, Copy)]
pub struct RenderToc;

impl HelperDef for RenderToc {
    fn call(&self, _h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {

        // get value from context data
        // rc.get_path() is current json parent path, you should always use it like this
        // param is the key of value you want to display
        let chapters = rc.evaluate_absolute("chapters")
            .and_then(|c| {
                          serde_json::value::from_value::<Vec<BTreeMap<String, String>>>(c.clone())
                              .map_err(|_| RenderError::new("Could not decode the JSON data"))
                      })?;
        let current = rc.evaluate_absolute("path")?
            .as_str()
            .ok_or(RenderError::new("Type error for `path`, string expected"))?
            .replace("\"", "");

        rc.writer.write_all("
".as_bytes())?;

        let mut current_level = 1;

        for item in chatpers {

            // Spacer
            if item.get("spacer").is_some() {
                rc.writer
                    .write_all("

".as_bytes())?;
                continue;
            }

            let level = if let Some(s) = item.get("section") {
                s.matches(".").count()
            } else {
                1
            };

            if level > current_level {
                while level > current_level {
                    rc.writer.write_all("
".as_bytes())?;
                    rc.writer.write_all("
".as_bytes())?;
                    current_level += 1;
                }
                rc.writer.write_all("
".as_bytes())?;
            } else if level < current_level {
                while level < current_level {
                    rc.writer.write_all("
".as_bytes())?;
                    rc.writer.write_all("
".as_bytes())?;
                    current_level -= 1;
                }
                rc.writer.write_all("
".as_bytes())?;
            } else {
                rc.writer.write_all("
".as_bytes())?;
            }

            // Link
            let path_exists = if let Some(path) = item.get("path") {
                if !path.is_empty() {
                    rc.writer.write_all("<a href=\"".as_bytes())?;

                    let tmp = Path::new(item.get("path").expect("Error: path should be Some(_)"))
                        .with_extension("html")
                        .to_str()
                        .unwrap()
                        // Hack for windows who tends to use `\` as separator instead of `/`
                        .replace("\\", "/");

                    // Add link
                    rc.writer.write_all(tmp.as_bytes())?;
                    rc.writer.write_all("\"".as_bytes())?;

                    if path == &current {
                        rc.writer.write_all(" class=\"active\"".as_bytes())?;
                    }

                    rc.writer.write_all(">".as_bytes())?;
                    true
                } else {
                    false
                }
            } else {
                false
            };

            // Section does not necessarily exist
            if let Some(section) = item.get("section") {
                rc.writer.write_all("".as_bytes())?;
                rc.writer.write_all(section.as_bytes())?;
                rc.writer.write_all(" ".as_bytes())?;
            }

            if let Some(name) = item.get("name") {
                // Render only inline code blocks

                // filter all events that are not inline code blocks
                let parser = Parser::new(name).filter(|event| match *event {
                                                          Event::Start(Tag::Code) |
                                                          Event::End(Tag::Code) |
                                                          Event::InlineHtml(_) |
                                                          Event::Text(_) => true,
                                                          _ => false,
                                                      });

                // render markdown to html
                let mut markdown_parsed_name = String::with_capacity(name.len() * 3 / 2);
                html::push_html(&mut markdown_parsed_name, parser);

                // write to the handlebars template
                rc.writer.write_all(markdown_parsed_name.as_bytes())?;
            }

            if path_exists {
                rc.writer.write_all("".as_bytes())?;
            }

            rc.writer.write_all("
".as_bytes())?;

        }
        while current_level > 1 {
            rc.writer.write_all("
".as_bytes())?;
            rc.writer.write_all("
".as_bytes())?;
            current_level -= 1;
        }

        rc.writer.write_all("
".as_bytes())?;
        Ok(())
    }
}
<commit_msg>(fix) some merge issue<commit_after>use std::path::Path;
use std::collections::BTreeMap;

use serde_json;
use handlebars::{Handlebars, HelperDef, RenderError, RenderContext, Helper};
use pulldown_cmark::{Parser, html, Event, Tag};

// Handlebars helper to construct TOC
#[derive(Clone, Copy)]
pub struct RenderToc;

impl HelperDef for RenderToc {
    fn call(&self, _h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {

        // get value from context data
        // rc.get_path() is current json parent path, you should always use it like this
        // param is the key of value you want to display
        let chapters = rc.evaluate_absolute("chapters")
            .and_then(|c| {
                          serde_json::value::from_value::<Vec<BTreeMap<String, String>>>(c.clone())
                              .map_err(|_| RenderError::new("Could not decode the JSON data"))
                      })?;
        let current = rc.evaluate_absolute("path")?
            .as_str()
            .ok_or(RenderError::new("Type error for `path`, string expected"))?
            .replace("\"", "");

        rc.writer.write_all("
".as_bytes())?;

        let mut current_level = 1;

        for item in chapters {

            // Spacer
            if item.get("spacer").is_some() {
                rc.writer
                    .write_all("

".as_bytes())?;
                continue;
            }

            let level = if let Some(s) = item.get("section") {
                s.matches(".").count()
            } else {
                1
            };

            if level > current_level {
                while level > current_level {
                    rc.writer.write_all("
".as_bytes())?;
                    rc.writer.write_all("
".as_bytes())?;
                    current_level += 1;
                }
                rc.writer.write_all("
".as_bytes())?;
            } else if level < current_level {
                while level < current_level {
                    rc.writer.write_all("
".as_bytes())?;
                    rc.writer.write_all("
".as_bytes())?;
                    current_level -= 1;
                }
                rc.writer.write_all("
".as_bytes())?;
            } else {
                rc.writer.write_all("
".as_bytes())?;
            }

            // Link
            let path_exists = if let Some(path) = item.get("path") {
                if !path.is_empty() {
                    rc.writer.write_all("<a href=\"".as_bytes())?;

                    let tmp = Path::new(item.get("path").expect("Error: path should be Some(_)"))
                        .with_extension("html")
                        .to_str()
                        .unwrap()
                        // Hack for windows who tends to use `\` as separator instead of `/`
                        .replace("\\", "/");

                    // Add link
                    rc.writer.write_all(tmp.as_bytes())?;
                    rc.writer.write_all("\"".as_bytes())?;

                    if path == &current {
                        rc.writer.write_all(" class=\"active\"".as_bytes())?;
                    }

                    rc.writer.write_all(">".as_bytes())?;
                    true
                } else {
                    false
                }
            } else {
                false
            };

            // Section does not necessarily exist
            if let Some(section) = item.get("section") {
                rc.writer.write_all("".as_bytes())?;
                rc.writer.write_all(section.as_bytes())?;
                rc.writer.write_all(" ".as_bytes())?;
            }

            if let Some(name) = item.get("name") {
                // Render only inline code blocks

                // filter all events that are not inline code blocks
                let parser = Parser::new(name).filter(|event| match *event {
                                                          Event::Start(Tag::Code) |
                                                          Event::End(Tag::Code) |
                                                          Event::InlineHtml(_) |
                                                          Event::Text(_) => true,
                                                          _ => false,
                                                      });

                // render markdown to html
                let mut markdown_parsed_name = String::with_capacity(name.len() * 3 / 2);
                html::push_html(&mut markdown_parsed_name, parser);

                // write to the handlebars template
                rc.writer.write_all(markdown_parsed_name.as_bytes())?;
            }

            if path_exists {
                rc.writer.write_all("".as_bytes())?;
            }

            rc.writer.write_all("
".as_bytes())?;

        }
        while current_level > 1 {
            rc.writer.write_all("
".as_bytes())?;
            rc.writer.write_all("
".as_bytes())?;
            current_level -= 1;
        }

        rc.writer.write_all("
".as_bytes())?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::fmt::{Debug, Formatter};
use std::fmt::Result as FMTResult;
use std::rc::Rc;
use std::cell::RefCell;

use clap::ArgMatches;
use regex::Regex;

mod header;

use module::Module;
use runtime::Runtime;
use storage::file::File;
use storage::parser::Parser;
use storage::json::parser::JsonHeaderParser;
use module::helpers::cli::create_tag_filter;
use module::helpers::cli::create_hash_filter;
use module::helpers::cli::create_text_header_field_grep_filter;
use module::helpers::cli::create_content_grep_filter;
use module::helpers::cli::CliFileFilter;

pub struct Notes<'a> {
    rt: &'a Runtime<'a>,
}

impl<'a> Notes<'a> {

    pub fn new(rt: &'a Runtime<'a>) -> Notes<'a> {
        Notes {
            rt: rt,
        }
    }

    fn command_add(&self, matches: &ArgMatches) -> bool {
        use std::process::exit;
        use self::header::build_header;

        let parser = Parser::new(JsonHeaderParser::new(None));
        let name   = matches.value_of("name")
                            .map(String::from)
                            .unwrap_or(String::from(""));
        let tags   = matches.value_of("tags")
                            .and_then(|s| Some(s.split(",").map(String::from).collect()))
                            .unwrap_or(vec![]);

        debug!("Building header with");
        debug!("    name = '{:?}'", name);
        debug!("    tags = '{:?}'", tags);
        let header = build_header(name, tags);

        let fileid = self.rt.store().new_file_with_header(self, header);
        self.rt
            .store()
            .load(self, &parser, &fileid)
            .and_then(|file| {
                info!("Created file in memory: {}", fileid);
                Some(self.rt.store().persist(&parser, file))
            })
            .unwrap_or(false)
    }

    fn command_list(&self, matches: &ArgMatches) -> bool {
        use ui::file::{FilePrinter, TablePrinter};
        use std::ops::Deref;
        use self::header::get_name_from_header;
        use self::header::get_tags_from_header;
        use std::process::exit;
        use module::helpers::cli::CliFileFilter;

        let parser  = Parser::new(JsonHeaderParser::new(None));

        let filter = {
            let hash_filter = create_hash_filter(matches, "id", true);
            let head_filter = create_text_header_field_grep_filter(matches, "match", "NAME", true);
            let text_filter = create_content_grep_filter(matches, "match", true);
            let tags_filter = create_tag_filter(matches, "tags", true);
            hash_filter.or(Box::new(head_filter)).and(Box::new(text_filter)).and(Box::new(tags_filter))
        };

        let printer = TablePrinter::new(self.rt.is_verbose(), self.rt.is_debugging());

        printer.print_files_custom(
            self.rt.store()
                    .load_for_module(self, &parser)
                    .into_iter()
                    .filter(|f| filter.filter_file(f)),
            &|file| {
                let fl      = file.deref().borrow();
                let hdr     = fl.header();
                let name    = get_name_from_header(hdr);
                let tags    = get_tags_from_header(hdr);

                debug!("Custom printer field: name = '{:?}'", name);
                debug!("Custom printer field: tags = '{:?}'", tags);

                vec![name, tags.join(", ")]
            }
        );
        true
    }

    fn command_remove(&self, matches: &ArgMatches) -> bool {
        unimplemented!()
    }

    fn command_add_tags(&self, matches: &ArgMatches) -> bool {
        unimplemented!()
    }

    fn command_rm_tags(&self, matches: &ArgMatches) -> bool {
        unimplemented!()
    }

    fn command_set_tags(&self, matches: &ArgMatches) -> bool {
        unimplemented!()
    }

}

impl<'a> Module<'a> for Notes<'a> {

    fn exec(&self, matches: &ArgMatches) -> bool {
        match matches.subcommand_name() {
            Some("add") => {
                self.command_add(matches.subcommand_matches("add").unwrap())
            },

            Some("list") => {
                self.command_list(matches.subcommand_matches("list").unwrap())
            },

            Some("remove") => {
                self.command_remove(matches.subcommand_matches("remove").unwrap())
            },

            Some("add_tags") => {
                self.command_add_tags(matches.subcommand_matches("add_tags").unwrap())
            },

            Some("rm_tags") => {
                self.command_rm_tags(matches.subcommand_matches("rm_tags").unwrap())
            },

            Some("set_tags") => {
                self.command_set_tags(matches.subcommand_matches("set_tags").unwrap())
            },

            Some(_) | None => {
                info!("No command given, doing nothing");
                false
            },
        }
    }

    fn name(&self) -> &'static str{
        "notes"
    }

}

impl<'a> Debug for Notes<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> FMTResult {
        write!(fmt, "[Module][Notes]");
        Ok(())
    }

}
<commit_msg>Notes: Implement command_remove<commit_after>use std::fmt::{Debug, Formatter};
use std::fmt::Result as FMTResult;
use std::rc::Rc;
use std::cell::RefCell;
use std::ops::Deref;

use clap::ArgMatches;
use regex::Regex;

mod header;

use module::Module;
use runtime::Runtime;
use storage::file::File;
use storage::parser::Parser;
use storage::json::parser::JsonHeaderParser;
use module::helpers::cli::create_tag_filter;
use module::helpers::cli::create_hash_filter;
use module::helpers::cli::create_text_header_field_grep_filter;
use module::helpers::cli::create_content_grep_filter;
use module::helpers::cli::CliFileFilter;

pub struct Notes<'a> {
    rt: &'a Runtime<'a>,
}

impl<'a> Notes<'a> {

    pub fn new(rt: &'a Runtime<'a>) -> Notes<'a> {
        Notes {
            rt: rt,
        }
    }

    fn command_add(&self, matches: &ArgMatches) -> bool {
        use std::process::exit;
        use self::header::build_header;

        let parser = Parser::new(JsonHeaderParser::new(None));
        let name   = matches.value_of("name")
                            .map(String::from)
                            .unwrap_or(String::from(""));
        let tags   = matches.value_of("tags")
                            .and_then(|s| Some(s.split(",").map(String::from).collect()))
                            .unwrap_or(vec![]);

        debug!("Building header with");
        debug!("    name = '{:?}'", name);
        debug!("    tags = '{:?}'", tags);
        let header = build_header(name, tags);

        let fileid = self.rt.store().new_file_with_header(self, header);
        self.rt
            .store()
            .load(self, &parser, &fileid)
            .and_then(|file| {
                info!("Created file in memory: {}", fileid);
                Some(self.rt.store().persist(&parser, file))
            })
            .unwrap_or(false)
    }

    fn command_list(&self, matches: &ArgMatches) -> bool {
        use ui::file::{FilePrinter, TablePrinter};
        use self::header::get_name_from_header;
        use self::header::get_tags_from_header;
        use std::process::exit;
        use module::helpers::cli::CliFileFilter;

        let parser  = Parser::new(JsonHeaderParser::new(None));

        let filter = {
            let hash_filter = create_hash_filter(matches, "id", true);
            let head_filter = create_text_header_field_grep_filter(matches, "match", "NAME", true);
            let text_filter = create_content_grep_filter(matches, "match", true);
            let tags_filter = create_tag_filter(matches, "tags", true);
            hash_filter.or(Box::new(head_filter)).and(Box::new(text_filter)).and(Box::new(tags_filter))
        };

        let printer = TablePrinter::new(self.rt.is_verbose(), self.rt.is_debugging());

        printer.print_files_custom(
            self.rt.store()
                    .load_for_module(self, &parser)
                    .into_iter()
                    .filter(|f| filter.filter_file(f)),
            &|file| {
                let fl      = file.deref().borrow();
                let hdr     = fl.header();
                let name    = get_name_from_header(hdr);
                let tags    = get_tags_from_header(hdr);

                debug!("Custom printer field: name = '{:?}'", name);
                debug!("Custom printer field: tags = '{:?}'", tags);

                vec![name, tags.join(", ")]
            }
        );
        true
    }

    fn command_remove(&self, matches: &ArgMatches) -> bool {
        let parser = Parser::new(JsonHeaderParser::new(None));

        let filter = {
            let hash_filter = create_hash_filter(matches, "id", false);
            let text_filter = create_text_header_field_grep_filter(matches, "match", "URL", false);
            let tags_filter = create_tag_filter(matches, "tags", false);
            hash_filter.or(Box::new(text_filter)).or(Box::new(tags_filter))
        };

        let result = self.rt
            .store()
            .load_for_module(self, &parser)
            .iter()
            .filter(|file| filter.filter_file(file))
            .map(|file| {
                debug!("File loaded, can remove now: {:?}", file);
                let f = file.deref().borrow();
                self.rt.store().remove(f.id().clone())
            })
            .fold((0, 0), |acc, succeeded| {
                let (worked, failed) = acc;
                if succeeded {
                    (worked + 1, failed)
                } else {
                    (worked, failed + 1)
                }
            });

        let (worked, failed) = result;

        info!("Removing succeeded for {} files", worked);
        info!("Removing failed for {} files", failed);

        return failed == 0;
    }

    fn command_add_tags(&self, matches: &ArgMatches) -> bool {
        unimplemented!()
    }

    fn command_rm_tags(&self, matches: &ArgMatches) -> bool {
        unimplemented!()
    }

    fn command_set_tags(&self, matches: &ArgMatches) -> bool {
        unimplemented!()
    }

}

impl<'a> Module<'a> for Notes<'a> {

    fn exec(&self, matches: &ArgMatches) -> bool {
        match matches.subcommand_name() {
            Some("add") => {
                self.command_add(matches.subcommand_matches("add").unwrap())
            },

            Some("list") => {
                self.command_list(matches.subcommand_matches("list").unwrap())
            },

            Some("remove") => {
                self.command_remove(matches.subcommand_matches("remove").unwrap())
            },

            Some("add_tags") => {
                self.command_add_tags(matches.subcommand_matches("add_tags").unwrap())
            },

            Some("rm_tags") => {
                self.command_rm_tags(matches.subcommand_matches("rm_tags").unwrap())
            },

            Some("set_tags") => {
                self.command_set_tags(matches.subcommand_matches("set_tags").unwrap())
            },

            Some(_) | None => {
                info!("No command given, doing nothing");
                false
            },
        }
    }

    fn name(&self) -> &'static str{
        "notes"
    }

}

impl<'a> Debug for Notes<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> FMTResult {
        write!(fmt, "[Module][Notes]");
        Ok(())
    }

}
<|endoftext|>


<commit_before>#![crate_name = "golf"]
#![no_std]
#![no_main]
#![feature(lang_items)]

extern crate drivers;
extern crate hotel;
extern crate hil;
extern crate main;

#[macro_use]
pub mod io;

use main::{Chip, MPU, Platform};

unsafe fn load_processes() -> &'static mut [Option<main::process::Process<'static>>] {
    extern {
        /// Beginning of the ROM region containing app images.
        static _sapps : u8;
    }

    const NUM_PROCS: usize = 2;

    #[link_section = ".app_memory"]
    static mut MEMORIES: [[u8; 8192]; NUM_PROCS] = [[0; 8192]; NUM_PROCS];

    static mut processes: [Option<main::process::Process<'static>>; NUM_PROCS] = [None, None];

    let mut addr = &_sapps as *const u8;
    for i in 0..NUM_PROCS {
        // The first member of the LoadInfo header contains the total size of each process image. A
        // sentinel value of 0 (invalid because it's smaller than the header itself) is used to
        // mark the end of the list of processes.
        let total_size = *(addr as *const usize);
        if total_size == 0 {
            break;
        }

        let process = &mut processes[i];
        let memory = &mut MEMORIES[i];
        *process = Some(main::process::Process::create(addr, total_size, memory));
        // TODO: panic if loading failed?

        addr = addr.offset(total_size as isize);
    }

    if *(addr as *const usize) != 0 {
        panic!("Exceeded maximum NUM_PROCS.");
    }

    &mut processes
}

pub struct Golf {
    gpio: &'static drivers::gpio::GPIO<'static, hotel::gpio::GPIOPin>
}

macro_rules! static_init {
   ($V:ident : $T:ty = $e:expr) => {
        let $V : &mut $T = {
            use core::mem::transmute;
            // Waiting out for size_of to be available at compile-time to avoid
            // hardcoding an abitrary large size...
            static mut BUF : [u8; 1024] = [0; 1024];
            let mut tmp : &mut $T = transmute(&mut BUF);
            *tmp = $e;
            tmp
        };
   }
}

#[no_mangle]
pub unsafe fn reset_handler() {
    hotel::init();

    let timer = {
        use hotel::pmu::*;
        use hotel::timeus::Timeus;
        Clock::new(PeripheralClock::Bank1(PeripheralClock1::TimeUs0Timer)).enable();
        let timer = Timeus::new(0);
        timer
    };

    timer.start();
    let start = timer.now();

    {
        use hotel::pmu::*;
        Clock::new(PeripheralClock::Bank0(PeripheralClock0::Gpio0)).enable();
        let pinmux = &mut *hotel::pinmux::PINMUX;
        pinmux.diob0.select.set(hotel::pinmux::Function::Gpio0Gpio0);
    }

    static_init!(gpio_pins : [&'static hotel::gpio::GPIOPin; 1] =
        [ &hotel::gpio::PORT0.pins[0] ]
    );

    static_init!(gpio : drivers::gpio::GPIO<'static, hotel::gpio::GPIOPin> =
                 drivers::gpio::GPIO::new(gpio_pins));

    static_init!(platform : Golf = Golf {
        gpio: gpio
    });

    let end = timer.now();

    println!("Hello from Rust! Initialization took {} tics.", end.wrapping_sub(start));

    let mut chip = hotel::chip::Hotel::new();
    chip.mpu().enable_mpu();

    main::main(platform, &mut chip, load_processes());
}

impl Platform for Golf {
    fn with_driver<F, R>(&mut self, driver_num: usize, f: F) -> R where
            F: FnOnce(Option<&main::Driver>) -> R {
        match driver_num {
            1 => f(Some(self.gpio)),
            _ => f(None)
        }
    }
}

<commit_msg>Initialize USB on golf<commit_after>#![crate_name = "golf"]
#![no_std]
#![no_main]
#![feature(lang_items)]

extern crate drivers;
extern crate hotel;
extern crate hil;
extern crate main;

#[macro_use]
pub mod io;

use main::{Chip, MPU, Platform};

unsafe fn load_processes() -> &'static mut [Option<main::process::Process<'static>>] {
    extern {
        /// Beginning of the ROM region containing app images.
        static _sapps : u8;
    }

    const NUM_PROCS: usize = 2;

    #[link_section = ".app_memory"]
    static mut MEMORIES: [[u8; 8192]; NUM_PROCS] = [[0; 8192]; NUM_PROCS];

    static mut processes: [Option<main::process::Process<'static>>; NUM_PROCS] = [None, None];

    let mut addr = &_sapps as *const u8;
    for i in 0..NUM_PROCS {
        // The first member of the LoadInfo header contains the total size of each process image. A
        // sentinel value of 0 (invalid because it's smaller than the header itself) is used to
        // mark the end of the list of processes.
        let total_size = *(addr as *const usize);
        if total_size == 0 {
            break;
        }

        let process = &mut processes[i];
        let memory = &mut MEMORIES[i];
        *process = Some(main::process::Process::create(addr, total_size, memory));
        // TODO: panic if loading failed?

        addr = addr.offset(total_size as isize);
    }

    if *(addr as *const usize) != 0 {
        panic!("Exceeded maximum NUM_PROCS.");
    }

    &mut processes
}

pub struct Golf {
    gpio: &'static drivers::gpio::GPIO<'static, hotel::gpio::GPIOPin>
}

macro_rules! static_init {
   ($V:ident : $T:ty = $e:expr) => {
        let $V : &mut $T = {
            use core::mem::transmute;
            // Waiting out for size_of to be available at compile-time to avoid
            // hardcoding an abitrary large size...
            static mut BUF : [u8; 1024] = [0; 1024];
            let mut tmp : &mut $T = transmute(&mut BUF);
            *tmp = $e;
            tmp
        };
   }
}

#[no_mangle]
pub unsafe fn reset_handler() {
    hotel::init();

    let timer = {
        use hotel::pmu::*;
        use hotel::timeus::Timeus;
        Clock::new(PeripheralClock::Bank1(PeripheralClock1::TimeUs0Timer)).enable();
        let timer = Timeus::new(0);
        timer
    };

    timer.start();
    let start = timer.now();

    {
        use hotel::pmu::*;
        Clock::new(PeripheralClock::Bank0(PeripheralClock0::Gpio0)).enable();
        let pinmux = &mut *hotel::pinmux::PINMUX;
        pinmux.diob0.select.set(hotel::pinmux::Function::Gpio0Gpio0);
    }

    static_init!(gpio_pins : [&'static hotel::gpio::GPIOPin; 1] =
        [ &hotel::gpio::PORT0.pins[0] ]
    );

    static_init!(gpio : drivers::gpio::GPIO<'static, hotel::gpio::GPIOPin> =
                 drivers::gpio::GPIO::new(gpio_pins));

    static_init!(platform : Golf = Golf {
        gpio: gpio
    });

    hotel::usb::USB0.init(&mut hotel::usb::OUT_DESCRIPTORS,
                          &mut hotel::usb::OUT_BUFFERS,
                          &mut hotel::usb::IN_DESCRIPTORS,
                          &mut hotel::usb::IN_BUFFERS,
                          hotel::usb::PHY::A,
                          None, Some(0x0011), Some(0x7788));

    let end = timer.now();

    println!("Hello from Rust! Initialization took {} tics.", end.wrapping_sub(start));

    let mut chip = hotel::chip::Hotel::new();
    chip.mpu().enable_mpu();

    main::main(platform, &mut chip, load_processes());
}

impl Platform for Golf {
    fn with_driver<F, R>(&mut self, driver_num: usize, f: F) -> R where
            F: FnOnce(Option<&main::Driver>) -> R {
        match driver_num {
            1 => f(Some(self.gpio)),
            _ => f(None)
        }
    }
}

<|endoftext|>


<commit_before>pub mod tcp_input;
pub mod tls_input;

use flowgger::decoder::Decoder;
use flowgger::encoder::Encoder;
use std::sync::mpsc::SyncSender;

pub trait CloneBoxedInput {
    fn clone_boxed<'a>(&self) -> Box<Input + Send + 'a> where Self: 'a;
}

impl<T: Input + Clone + Send> CloneBoxedInput for T {
    fn clone_boxed<'a>(&self) -> Box<Input + Send + 'a> where Self: 'a {
        Box::new(self.clone())
    }
}

impl Clone for Box<Input> {
    fn clone(&self) -> Box<Input> {
        self.clone_boxed()
    }
}

pub trait Input : CloneBoxedInput {
    fn accept(&self, tx: SyncSender<Vec<u8>>, decoder: Box<Decoder + Send>, encoder: Box<Encoder + Send>);
}
<commit_msg>The Input trait doesn't need to be clonable/sendable<commit_after>pub mod tcp_input;
pub mod tls_input;

use flowgger::decoder::Decoder;
use flowgger::encoder::Encoder;
use std::sync::mpsc::SyncSender;

pub trait Input {
    fn accept(&self, tx: SyncSender<Vec<u8>>, decoder: Box<Decoder + Send>, encoder: Box<Encoder + Send>);
}
<|endoftext|>


<commit_before>use crate::config::types::Mode;
use crate::errors::*;
use crate::process_handling::execute_test;
use crate::ptrace_control::*;
use crate::Config;
use crate::TestBinary;
use crate::TestHandle;
use nix::errno::Errno;
use nix::libc::{c_int, c_long};
use nix::sched::*;
use nix::unistd::*;
use std::ffi::{CStr, CString};
use std::path::Path;
use tracing::{info, warn};

#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "arm"))]
type Persona = c_long;

const ADDR_NO_RANDOMIZE: Persona = 0x004_0000;
const GET_PERSONA: Persona = 0xFFFF_FFFF;

mod ffi {
    use nix::libc::{c_int, c_long};

    extern "C" {
        pub fn personality(persona: c_long) -> c_int;
    }
}

/// Returns the coverage statistics for a test executable in the given workspace
pub fn get_test_coverage(
    test: &TestBinary,
    config: &Config,
    ignored: bool,
) -> Result<Option<TestHandle>, RunError> {
    if !test.path().exists() {
        warn!("Test at {} doesn't exist", test.path().display());
        return Ok(None);
    }
    let cpus = Some(num_cpus::get());
    if let Err(e) = limit_affinity() {
        warn!("Failed to set processor affinity {}", e);
    }
    unsafe {
        match fork() {
            Ok(ForkResult::Parent { child }) => Ok(Some(TestHandle::Id(child))),
            Ok(ForkResult::Child) => {
                let bin_type = match config.command {
                    Mode::Test => "test",
                    Mode::Build => "binary",
                };
                info!("Launching {}", bin_type);
                execute_test(test, ignored, config, cpus)?;
                Ok(None)
            }
            Err(err) => Err(RunError::TestCoverage(format!(
                "Failed to run test {}, Error: {}",
                test.path().display(),
                err.to_string()
            ))),
        }
    }
}

fn personality(persona: Persona) -> nix::Result<c_int> {
    let ret = unsafe {
        Errno::clear();
        ffi::personality(persona)
    };
    match Errno::result(ret) {
        Ok(..) | Err(Errno::UnknownErrno) => Ok(ret),
        err @ Err(..) => err,
    }
}

fn disable_aslr() -> nix::Result<i32> {
    match personality(GET_PERSONA) {
        Ok(p) => match personality(i64::from(p) | ADDR_NO_RANDOMIZE) {
            ok @ Ok(_) => ok,
            err @ Err(..) => err,
        },
        err @ Err(..) => err,
    }
}

pub fn limit_affinity() -> nix::Result<()> {
    let this = Pid::this();
    // Get current affinity to be able to limit the cores to one of
    // those already in the affinity mask.
    let affinity = sched_getaffinity(this)?;
    let mut selected_cpu = 0;
    for i in 0..CpuSet::count() {
        if affinity.is_set(i)? {
            selected_cpu = i;
            break;
        }
    }
    let mut cpu_set = CpuSet::new();
    cpu_set.set(selected_cpu)?;
    sched_setaffinity(this, &cpu_set)
}

pub fn execute(
    test: &Path,
    argv: &[String],
    envar: &[(String, String)],
) -> Result<TestHandle, RunError> {
    let program = CString::new(test.display().to_string()).unwrap_or_default();
    disable_aslr().map_err(|e| RunError::TestRuntime(format!("ASLR disable failed: {}", e)))?;

    request_trace().map_err(|e| RunError::Trace(e.to_string()))?;

    let envar = envar
        .iter()
        .map(|(k, v)| CString::new(format!("{}={}", k, v).as_str()).unwrap_or_default())
        .collect::<Vec<CString>>();

    let argv = argv
        .iter()
        .map(|x| CString::new(x.as_str()).unwrap_or_default())
        .collect::<Vec<CString>>();

    let arg_ref = argv.iter().map(|x| x.as_ref()).collect::<Vec<&CStr>>();
    let env_ref = envar.iter().map(|x| x.as_ref()).collect::<Vec<&CStr>>();
    execve(&program, &arg_ref, &env_ref).map_err(|_| RunError::Internal)?;

    unreachable!();
}
<commit_msg>Apply affinity limit only to fork child (#899)<commit_after>use crate::config::types::Mode;
use crate::errors::*;
use crate::process_handling::execute_test;
use crate::ptrace_control::*;
use crate::Config;
use crate::TestBinary;
use crate::TestHandle;
use lazy_static::lazy_static;
use nix::errno::Errno;
use nix::libc::{c_int, c_long};
use nix::sched::*;
use nix::unistd::*;
use std::ffi::{CStr, CString};
use std::path::Path;
use tracing::{info, warn};

#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "arm"))]
type Persona = c_long;

lazy_static! {
    static ref NUM_CPUS: usize = num_cpus::get();
}

const ADDR_NO_RANDOMIZE: Persona = 0x004_0000;
const GET_PERSONA: Persona = 0xFFFF_FFFF;

mod ffi {
    use nix::libc::{c_int, c_long};

    extern "C" {
        pub fn personality(persona: c_long) -> c_int;
    }
}

/// Returns the coverage statistics for a test executable in the given workspace
pub fn get_test_coverage(
    test: &TestBinary,
    config: &Config,
    ignored: bool,
) -> Result<Option<TestHandle>, RunError> {
    if !test.path().exists() {
        warn!("Test at {} doesn't exist", test.path().display());
        return Ok(None);
    }
    let cpus = Some(*NUM_CPUS);
    if let Err(e) = limit_affinity() {
        warn!("Failed to set processor affinity {}", e);
    }

    unsafe {
        match fork() {
            Ok(ForkResult::Parent { child }) => Ok(Some(TestHandle::Id(child))),
            Ok(ForkResult::Child) => {
                let bin_type = match config.command {
                    Mode::Test => "test",
                    Mode::Build => "binary",
                };
                info!("Launching {}", bin_type);
                execute_test(test, ignored, config, cpus)?;
                Ok(None)
            }
            Err(err) => Err(RunError::TestCoverage(format!(
                "Failed to run test {}, Error: {}",
                test.path().display(),
                err.to_string()
            ))),
        }
    }
}

fn personality(persona: Persona) -> nix::Result<c_int> {
    let ret = unsafe {
        Errno::clear();
        ffi::personality(persona)
    };
    match Errno::result(ret) {
        Ok(..) | Err(Errno::UnknownErrno) => Ok(ret),
        err @ Err(..) => err,
    }
}

fn disable_aslr() -> nix::Result<i32> {
    match personality(GET_PERSONA) {
        Ok(p) => match personality(i64::from(p) | ADDR_NO_RANDOMIZE) {
            ok @ Ok(_) => ok,
            err @ Err(..) => err,
        },
        err @ Err(..) => err,
    }
}

pub fn limit_affinity() -> nix::Result<()> {
    let this = Pid::this();
    // Get current affinity to be able to limit the cores to one of
    // those already in the affinity mask.
    let affinity = sched_getaffinity(this)?;
    let mut selected_cpu = 0;
    for i in 0..CpuSet::count() {
        if affinity.is_set(i)? {
            selected_cpu = i;
            break;
        }
    }
    let mut cpu_set = CpuSet::new();
    cpu_set.set(selected_cpu)?;
    sched_setaffinity(this, &cpu_set)
}

pub fn execute(
    test: &Path,
    argv: &[String],
    envar: &[(String, String)],
) -> Result<TestHandle, RunError> {
    let program = CString::new(test.display().to_string()).unwrap_or_default();
    disable_aslr().map_err(|e| RunError::TestRuntime(format!("ASLR disable failed: {}", e)))?;

    request_trace().map_err(|e| RunError::Trace(e.to_string()))?;

    let envar = envar
        .iter()
        .map(|(k, v)| CString::new(format!("{}={}", k, v).as_str()).unwrap_or_default())
        .collect::<Vec<CString>>();

    let argv = argv
        .iter()
        .map(|x| CString::new(x.as_str()).unwrap_or_default())
        .collect::<Vec<CString>>();

    let arg_ref = argv.iter().map(|x| x.as_ref()).collect::<Vec<&CStr>>();
    let env_ref = envar.iter().map(|x| x.as_ref()).collect::<Vec<&CStr>>();
    execve(&program, &arg_ref, &env_ref).map_err(|_| RunError::Internal)?;

    unreachable!();
}
<|endoftext|>