<commit_before>use rustwlc::{input, Point, ResizeEdge, Geometry,
              RESIZE_LEFT, RESIZE_RIGHT, RESIZE_TOP, RESIZE_BOTTOM,
              RESIZE_TOPLEFT, RESIZE_TOPRIGHT, RESIZE_BOTTOMLEFT, RESIZE_BOTTOMRIGHT,
};

use super::super::{Action, Direction, LayoutTree, TreeError};
use super::super::commands::{CommandResult};
use super::super::core::container::{ContainerType, MIN_SIZE};
use uuid::Uuid;

#[derive(Debug, Clone, Copy)]
pub enum ResizeErr {
    /// Expected the node associated with the UUID to be floating.
    ExpectedFloating(Uuid),
    /// Expected the node associated with the UUID to not be floating
    ExpectedNotFloating(Uuid)
}

impl LayoutTree {
    /// Updates pointer position for the node behind the id,
    /// in the same direction as the edge.
    pub fn update_pointer_pos(&self, id: Uuid, edge: ResizeEdge) -> CommandResult {
        let container = try!(self.lookup(id));
        let Geometry { mut origin, size } = container.get_geometry()
            .expect("Container had no geometry");
        drop(container);
        if edge.contains(RESIZE_TOPLEFT) {
            input::pointer::set_position(origin);
        } else if edge.contains(RESIZE_TOPRIGHT) {
            origin.x += size.w as i32;
            input::pointer::set_position(origin);
        } else if edge.contains(RESIZE_BOTTOMLEFT) {
            origin.y += size.h as i32;
            input::pointer::set_position(origin);
        } else if edge.contains(RESIZE_BOTTOMRIGHT) {
            origin.x += size.w as i32;
            origin.y += size.h as i32;
            input::pointer::set_position(origin);
        }
        Ok(())
    }

    /// Resizes a floating container. If the container was not floating, an Err is returned.
    pub fn resize_floating(&mut self, id: Uuid, edge: ResizeEdge, pointer: Point,
                           action: &mut Action) -> CommandResult {
        {
            let container = try!(self.lookup_mut(id));
            if !container.floating() {
                return Err(TreeError::Resize(ResizeErr::ExpectedFloating(container.get_id())))
            }
            match container.get_type() {
                ContainerType::View | ContainerType::Container => {},
                _ => return Err(TreeError::UuidWrongType(container.get_id(),
                                                        vec!(ContainerType::View)))
            }
            let geo = container.get_geometry()
                .expect("Could not get geometry of the container");

            let new_geo = calculate_resize(geo, edge, pointer, action.grab);
            action.grab = pointer;
            container.set_geometry(edge, new_geo);
        }
        self.update_pointer_pos(id, edge)
    }

    pub fn resize_tiled(&mut self, id: Uuid, edge: ResizeEdge, pointer: Point,
                        action: &mut Action) -> CommandResult {
        // There can be multiple directions we are moving in, e.g up and left.
        let parent_id = try!(self.parent_of(id)).get_id();
        let dirs_moving_in = Direction::from_edge(edge);
        let siblings: Vec<Uuid> = dirs_moving_in.iter()
            .map(|dir| self.sibling_in_dir(id, *dir))
            // TODO There MUST be a better way to do something like this...
            .filter(|thing| thing.is_ok())
            .map(|thing| thing.unwrap())
            .collect();

        // Because we can't have multiple mutable references active in the tree at once,
        // first we modify the one the user is resizing and then adjust the siblings.
        {
            let container = try!(self.lookup_mut(id));
            if container.floating() {
                return Err(TreeError::Resize(ResizeErr::ExpectedNotFloating(container.get_id())))
            }
            match container.get_type() {
                ContainerType::View | ContainerType::Container => {},
                _ => return Err(TreeError::UuidWrongType(container.get_id(),
                                                        vec!(ContainerType::View)))
            }
            let geo = container.get_geometry()
                .expect("Could not get geometry of the container");
            let new_geo = calculate_resize(geo, edge, pointer, action.grab);
            container.set_geometry(edge, new_geo);
        }
        //....update parent?
        {
            let container = try!(self.lookup_mut(parent_id));
            if container.floating() {
                return Err(TreeError::Resize(ResizeErr::ExpectedNotFloating(container.get_id())))
            }
            match container.get_type() {
                ContainerType::View | ContainerType::Container => {},
                _ => return Err(TreeError::UuidWrongType(container.get_id(),
                                                          vec!(ContainerType::View)))
            }
            let geo = container.get_geometry()
                .expect("Could not get geometry of the container");
            let new_geo = calculate_resize(geo, edge, pointer, action.grab);
            container.set_geometry(edge, new_geo);

        }
        let parent_ix = self.tree.lookup_id(parent_id)
            .expect("Could not get the index of the parent");
        let parent_parent_ix = self.tree.parent_of(parent_ix)
            .expect("Could not get parent parent");
        self.layout(parent_parent_ix);
        // and now we mutate the siblings
        let reversed_dir: Vec<Direction> = dirs_moving_in.iter()
            .map(|dir| dir.reverse()).collect();
        let reversed_edge = Direction::to_edge(reversed_dir.as_slice());
        for sibling in siblings {
            // TODO Abstract, the only thing different from above
            // is the edge and the exact node we are doing the calc on
            let container = try!(self.lookup_mut(sibling));
            if container.floating() {
                return Err(TreeError::Resize(ResizeErr::ExpectedNotFloating(container.get_id())))
            }
            match container.get_type() {
                ContainerType::View | ContainerType::Container => {},
                _ => return Err(TreeError::UuidWrongType(container.get_id(),
                                                         vec!(ContainerType::View)))
            }
            let geo = container.get_geometry()
                .expect("Could not get geometry of the container");
            let new_geo = calculate_resize(geo, reversed_edge, pointer, action.grab);
            action.grab = pointer;
            container.set_geometry(reversed_edge, new_geo);
        }
        let node_ix = self.tree.lookup_id(id).unwrap();
        self.layout(node_ix);
        action.grab = pointer;
        self.update_pointer_pos(id, edge)
    }
}

/// Calculates what the new geometry is of a window.
/// Needs the geometry of the window, the edge direction the pointer is moving in,
/// the current position of the pointer, and the previous place the pointer was at.
fn calculate_resize(geo: Geometry, edge: ResizeEdge,
                    cur_pointer: Point, prev_pointer: Point) -> Geometry {
    let mut new_geo = geo.clone();
    let dx = cur_pointer.x - prev_pointer.x;
    let dy = cur_pointer.y - prev_pointer.y;
    if edge.contains(RESIZE_LEFT) {
        if dx < 0 {
            new_geo.size.w = geo.size.w.saturating_add(dx.abs() as u32);
        } else {
            new_geo.size.w = geo.size.w.saturating_sub(dx.abs() as u32);
        }
        new_geo.origin.x += dx;
    }
    else if edge.contains(RESIZE_RIGHT) {
        if dx < 0 {
            new_geo.size.w = geo.size.w.saturating_sub(dx.abs() as u32);
        } else {
            new_geo.size.w = geo.size.w.saturating_add(dx.abs() as u32);
        }
    }

    if edge.contains(RESIZE_TOP) {
        if dy < 0 {
            new_geo.size.h = geo.size.h.saturating_add(dy.abs() as u32);
        } else {
            new_geo.size.h = geo.size.h.saturating_sub(dy.abs() as u32);
        }
        new_geo.origin.y += dy;
    }
    else if edge.contains(RESIZE_BOTTOM) {
        if dy < 0 {
            new_geo.size.h = geo.size.h.saturating_sub(dy.abs() as u32);
        } else {
            new_geo.size.h = geo.size.h.saturating_add(dy.abs() as u32);
        }
    }

    if new_geo.size.w <= MIN_SIZE.w {
        new_geo.origin.x = geo.origin.x;
        new_geo.size.w = geo.size.w;
    }

    if new_geo.size.h <= MIN_SIZE.h {
        new_geo.origin.y = geo.origin.y;
        new_geo.size.h = geo.size.h;
    }
    new_geo
}
<commit_msg>Layout the entire tree, to fix the spacing issues<commit_after>use rustwlc::{input, Point, ResizeEdge, Geometry,
              RESIZE_LEFT, RESIZE_RIGHT, RESIZE_TOP, RESIZE_BOTTOM,
              RESIZE_TOPLEFT, RESIZE_TOPRIGHT, RESIZE_BOTTOMLEFT, RESIZE_BOTTOMRIGHT,
};

use super::super::{Action, Direction, LayoutTree, TreeError};
use super::super::commands::{CommandResult};
use super::super::core::container::{ContainerType, MIN_SIZE};
use uuid::Uuid;

#[derive(Debug, Clone, Copy)]
pub enum ResizeErr {
    /// Expected the node associated with the UUID to be floating.
    ExpectedFloating(Uuid),
    /// Expected the node associated with the UUID to not be floating
    ExpectedNotFloating(Uuid)
}

impl LayoutTree {
    /// Updates pointer position for the node behind the id,
    /// in the same direction as the edge.
    pub fn update_pointer_pos(&self, id: Uuid, edge: ResizeEdge) -> CommandResult {
        let container = try!(self.lookup(id));
        let Geometry { mut origin, size } = container.get_geometry()
            .expect("Container had no geometry");
        drop(container);
        if edge.contains(RESIZE_TOPLEFT) {
            input::pointer::set_position(origin);
        } else if edge.contains(RESIZE_TOPRIGHT) {
            origin.x += size.w as i32;
            input::pointer::set_position(origin);
        } else if edge.contains(RESIZE_BOTTOMLEFT) {
            origin.y += size.h as i32;
            input::pointer::set_position(origin);
        } else if edge.contains(RESIZE_BOTTOMRIGHT) {
            origin.x += size.w as i32;
            origin.y += size.h as i32;
            input::pointer::set_position(origin);
        }
        Ok(())
    }

    /// Resizes a floating container. If the container was not floating, an Err is returned.
    pub fn resize_floating(&mut self, id: Uuid, edge: ResizeEdge, pointer: Point,
                           action: &mut Action) -> CommandResult {
        {
            let container = try!(self.lookup_mut(id));
            if !container.floating() {
                return Err(TreeError::Resize(ResizeErr::ExpectedFloating(container.get_id())))
            }
            match container.get_type() {
                ContainerType::View | ContainerType::Container => {},
                _ => return Err(TreeError::UuidWrongType(container.get_id(),
                                                        vec!(ContainerType::View)))
            }
            let geo = container.get_geometry()
                .expect("Could not get geometry of the container");

            let new_geo = calculate_resize(geo, edge, pointer, action.grab);
            action.grab = pointer;
            container.set_geometry(edge, new_geo);
        }
        self.update_pointer_pos(id, edge)
    }

    pub fn resize_tiled(&mut self, id: Uuid, edge: ResizeEdge, pointer: Point,
                        action: &mut Action) -> CommandResult {
        // There can be multiple directions we are moving in, e.g up and left.
        let parent_id = try!(self.parent_of(id)).get_id();
        let dirs_moving_in = Direction::from_edge(edge);
        let siblings: Vec<Uuid> = dirs_moving_in.iter()
            .map(|dir| self.sibling_in_dir(id, *dir))
            // TODO There MUST be a better way to do something like this...
            .filter(|thing| thing.is_ok())
            .map(|thing| thing.unwrap())
            .collect();

        // Because we can't have multiple mutable references active in the tree at once,
        // first we modify the one the user is resizing and then adjust the siblings.
        {
            let container = try!(self.lookup_mut(id));
            if container.floating() {
                return Err(TreeError::Resize(ResizeErr::ExpectedNotFloating(container.get_id())))
            }
            match container.get_type() {
                ContainerType::View | ContainerType::Container => {},
                _ => return Err(TreeError::UuidWrongType(container.get_id(),
                                                        vec!(ContainerType::View)))
            }
            let geo = container.get_geometry()
                .expect("Could not get geometry of the container");
            let new_geo = calculate_resize(geo, edge, pointer, action.grab);
            container.set_geometry(edge, new_geo);
        }
        //....update parent?
        {
            let container = try!(self.lookup_mut(parent_id));
            if container.floating() {
                return Err(TreeError::Resize(ResizeErr::ExpectedNotFloating(container.get_id())))
            }
            match container.get_type() {
                ContainerType::View | ContainerType::Container => {},
                _ => return Err(TreeError::UuidWrongType(container.get_id(),
                                                          vec!(ContainerType::View)))
            }
            let geo = container.get_geometry()
                .expect("Could not get geometry of the container");
            let new_geo = calculate_resize(geo, edge, pointer, action.grab);
            container.set_geometry(edge, new_geo);

        }
        // and now we mutate the siblings
        let reversed_dir: Vec<Direction> = dirs_moving_in.iter()
            .map(|dir| dir.reverse()).collect();
        let reversed_edge = Direction::to_edge(reversed_dir.as_slice());
        for sibling in siblings {
            // TODO Abstract, the only thing different from above
            // is the edge and the exact node we are doing the calc on
            let container = try!(self.lookup_mut(sibling));
            if container.floating() {
                return Err(TreeError::Resize(ResizeErr::ExpectedNotFloating(container.get_id())))
            }
            match container.get_type() {
                ContainerType::View | ContainerType::Container => {},
                _ => return Err(TreeError::UuidWrongType(container.get_id(),
                                                         vec!(ContainerType::View)))
            }
            let geo = container.get_geometry()
                .expect("Could not get geometry of the container");
            let new_geo = calculate_resize(geo, reversed_edge, pointer, action.grab);
            action.grab = pointer;
            container.set_geometry(reversed_edge, new_geo);
        }
        let node_ix = self.tree.lookup_id(id)
            .expect("Could not find node index for an id");
        let workspace_ix = try!(self.tree.ancestor_of_type(node_ix,
                                                           ContainerType::Workspace)
                                .map_err(|err| TreeError::PetGraph(err)));
        self.layout(workspace_ix);
        action.grab = pointer;
        self.update_pointer_pos(id, edge)
    }
}

/// Calculates what the new geometry is of a window.
/// Needs the geometry of the window, the edge direction the pointer is moving in,
/// the current position of the pointer, and the previous place the pointer was at.
fn calculate_resize(geo: Geometry, edge: ResizeEdge,
                    cur_pointer: Point, prev_pointer: Point) -> Geometry {
    let mut new_geo = geo.clone();
    let dx = cur_pointer.x - prev_pointer.x;
    let dy = cur_pointer.y - prev_pointer.y;
    if edge.contains(RESIZE_LEFT) {
        if dx < 0 {
            new_geo.size.w = geo.size.w.saturating_add(dx.abs() as u32);
        } else {
            new_geo.size.w = geo.size.w.saturating_sub(dx.abs() as u32);
        }
        new_geo.origin.x += dx;
    }
    else if edge.contains(RESIZE_RIGHT) {
        if dx < 0 {
            new_geo.size.w = geo.size.w.saturating_sub(dx.abs() as u32);
        } else {
            new_geo.size.w = geo.size.w.saturating_add(dx.abs() as u32);
        }
    }

    if edge.contains(RESIZE_TOP) {
        if dy < 0 {
            new_geo.size.h = geo.size.h.saturating_add(dy.abs() as u32);
        } else {
            new_geo.size.h = geo.size.h.saturating_sub(dy.abs() as u32);
        }
        new_geo.origin.y += dy;
    }
    else if edge.contains(RESIZE_BOTTOM) {
        if dy < 0 {
            new_geo.size.h = geo.size.h.saturating_sub(dy.abs() as u32);
        } else {
            new_geo.size.h = geo.size.h.saturating_add(dy.abs() as u32);
        }
    }

    if new_geo.size.w <= MIN_SIZE.w {
        new_geo.origin.x = geo.origin.x;
        new_geo.size.w = geo.size.w;
    }

    if new_geo.size.h <= MIN_SIZE.h {
        new_geo.origin.y = geo.origin.y;
        new_geo.size.h = geo.size.h;
    }
    new_geo
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::system_module_names::{
    BLOCK_PROLOGUE, DIEM_BLOCK_MODULE, SCRIPT_PROLOGUE_NAME, USER_EPILOGUE_NAME,
};
use anyhow::{bail, Result};
use diem_types::{
    account_config,
    transaction::{SignedTransaction, TransactionPayload},
};
use move_core_types::{
    ident_str,
    identifier::{IdentStr, Identifier},
    language_storage::{ModuleId, ResourceKey, StructTag},
    resolver::MoveResolver,
    value::{serialize_values, MoveValue},
};
use read_write_set_dynamic::NormalizedReadWriteSetAnalysis;
use std::ops::Deref;

pub struct ReadWriteSetAnalysis(NormalizedReadWriteSetAnalysis);

const TRANSACTION_FEES_NAME: &IdentStr = ident_str!("TransactionFee");

pub fn add_on_functions_list() -> Vec<(ModuleId, Identifier)> {
    vec![
        (DIEM_BLOCK_MODULE.clone(), BLOCK_PROLOGUE.to_owned()),
        (
            account_config::constants::ACCOUNT_MODULE.clone(),
            SCRIPT_PROLOGUE_NAME.to_owned(),
        ),
        (
            account_config::constants::ACCOUNT_MODULE.clone(),
            USER_EPILOGUE_NAME.to_owned(),
        ),
    ]
}

impl ReadWriteSetAnalysis {
    /// Create a Diem transaction read/write set analysis from a generic Move module read/write set
    /// analysis
    pub fn new(rw: NormalizedReadWriteSetAnalysis) -> Self {
        ReadWriteSetAnalysis(rw)
    }

    /// Returns an overapproximation of the `ResourceKey`'s in global storage that will be written
    /// by `tx` if executed in state `blockchain_view`.
    /// Note: this will return both writes performed by the transaction prologue/epilogue and by its
    /// embedded payload.
    pub fn get_keys_written(
        &self,
        tx: &SignedTransaction,
        blockchain_view: &impl MoveResolver,
    ) -> Result<Vec<ResourceKey>> {
        self.get_concretized_keys_tx(tx, blockchain_view, true)
    }

    /// Returns an overapproximation of the `ResourceKey`'s in global storage that will be read
    /// by `tx` if executed in state `blockchain_view`.
    /// Note: this will return both reads performed by the transaction prologue/epilogue and by its
    /// embedded payload.
    pub fn get_keys_read(
        &self,
        tx: &SignedTransaction,
        blockchain_view: &impl MoveResolver,
    ) -> Result<Vec<ResourceKey>> {
        self.get_concretized_keys_tx(tx, blockchain_view, false)
    }

    fn get_concretized_keys_tx(
        &self,
        tx: &SignedTransaction,
        blockchain_view: &impl MoveResolver,
        is_write: bool,
    ) -> Result<Vec<ResourceKey>> {
        match tx.payload() {
            TransactionPayload::ScriptFunction(s) => {
                let signers = vec![tx.sender()];
                let gas_currency = account_config::type_tag_for_currency_code(
                    account_config::from_currency_code_string(tx.gas_currency_code())?,
                );
                let prologue_accesses = self.get_concretized_keys(
                    &account_config::constants::ACCOUNT_MODULE,
                    SCRIPT_PROLOGUE_NAME,
                    &signers,
                    &serialize_values(&vec![
                        MoveValue::U64(tx.sequence_number()),
                        MoveValue::vector_u8(tx.authenticator().sender().public_key_bytes()),
                        MoveValue::U64(tx.gas_unit_price()),
                        MoveValue::U64(tx.max_gas_amount()),
                        MoveValue::U64(tx.expiration_timestamp_secs()),
                        MoveValue::U8(tx.chain_id().id()),
                        MoveValue::vector_u8(vec![]), // script_hash; it's ignored
                    ]),
                    &[gas_currency.clone()],
                    blockchain_view,
                    is_write,
                )?;
                let mut epilogue_accesses = self.get_concretized_keys(
                    &account_config::constants::ACCOUNT_MODULE,
                    USER_EPILOGUE_NAME,
                    &signers,
                    &serialize_values(&vec![
                        MoveValue::U64(tx.sequence_number()),
                        MoveValue::U64(tx.gas_unit_price()),
                        MoveValue::U64(tx.max_gas_amount()),
                        MoveValue::U64(0), // gas_units_remaining
                    ]),
                    &[gas_currency.clone()],
                    blockchain_view,
                    is_write,
                )?;
                let mut script_accesses = self.get_concretized_keys(
                    s.module(),
                    s.function(),
                    &signers,
                    s.args(),
                    s.ty_args(),
                    blockchain_view,
                    is_write,
                )?;
                // Hack: remove GasFees accesses from epilogue if gas_price is zero. This is sound
                // to do as of Diem 1.4, but should be re-evaluated if the epilogue changes
                if tx.gas_unit_price() == 0 {
                    let tx_fees_tag = StructTag {
                        address: account_config::CORE_CODE_ADDRESS,
                        module: TRANSACTION_FEES_NAME.to_owned(),
                        name: TRANSACTION_FEES_NAME.to_owned(),
                        type_params: vec![gas_currency],
                    };
                    epilogue_accesses.retain(|r| r.type_() != &tx_fees_tag);
                }
                // combine prologue, epilogue, and script accesses, then dedup and return result
                script_accesses.extend(prologue_accesses);
                script_accesses.extend(epilogue_accesses);
                script_accesses.sort();
                script_accesses.dedup();
                Ok(script_accesses)
            }
            payload => {
                // TODO: support tx scripts here. Slightly tricky since we will need to run
                // analyzer on the fly
                bail!("Unsupported transaction payload type {:?}", payload)
            }
        }
    }
}

impl Deref for ReadWriteSetAnalysis {
    type Target = NormalizedReadWriteSetAnalysis;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
<commit_msg>[read-write-set] Return read and write at the same time to avoid double concretization.<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    adapter_common::PreprocessedTransaction,
    system_module_names::{
        BLOCK_PROLOGUE, DIEM_BLOCK_MODULE, SCRIPT_PROLOGUE_NAME, USER_EPILOGUE_NAME,
    },
};
use anyhow::{anyhow, bail, Result};
use diem_types::{
    account_config,
    transaction::{SignedTransaction, TransactionPayload},
};
use move_core_types::{
    ident_str,
    identifier::{IdentStr, Identifier},
    language_storage::{ModuleId, ResourceKey, StructTag},
    resolver::MoveResolver,
    value::{serialize_values, MoveValue},
};
use read_write_set_dynamic::NormalizedReadWriteSetAnalysis;
use std::ops::Deref;

pub struct ReadWriteSetAnalysis(NormalizedReadWriteSetAnalysis);

const TRANSACTION_FEES_NAME: &IdentStr = ident_str!("TransactionFee");

pub fn add_on_functions_list() -> Vec<(ModuleId, Identifier)> {
    vec![
        (DIEM_BLOCK_MODULE.clone(), BLOCK_PROLOGUE.to_owned()),
        (
            account_config::constants::ACCOUNT_MODULE.clone(),
            SCRIPT_PROLOGUE_NAME.to_owned(),
        ),
        (
            account_config::constants::ACCOUNT_MODULE.clone(),
            USER_EPILOGUE_NAME.to_owned(),
        ),
    ]
}

impl ReadWriteSetAnalysis {
    /// Create a Diem transaction read/write set analysis from a generic Move module read/write set
    /// analysis
    pub fn new(rw: NormalizedReadWriteSetAnalysis) -> Self {
        ReadWriteSetAnalysis(rw)
    }

    /// Returns an overapproximation of the `ResourceKey`'s in global storage that will be written
    /// by `tx` if executed in state `blockchain_view`.
    /// Note: this will return both writes performed by the transaction prologue/epilogue and by its
    /// embedded payload.
    pub fn get_keys_written(
        &self,
        tx: &SignedTransaction,
        blockchain_view: &impl MoveResolver,
    ) -> Result<Vec<ResourceKey>> {
        Ok(self
            .get_concretized_keys_user_transaction(tx, blockchain_view)?
            .1)
    }

    /// Returns an overapproximation of the `ResourceKey`'s in global storage that will be read
    /// by `tx` if executed in state `blockchain_view`.
    /// Note: this will return both reads performed by the transaction prologue/epilogue and by its
    /// embedded payload.
    pub fn get_keys_read(
        &self,
        tx: &SignedTransaction,
        blockchain_view: &impl MoveResolver,
    ) -> Result<Vec<ResourceKey>> {
        Ok(self
            .get_concretized_keys_user_transaction(tx, blockchain_view)?
            .0)
    }

    fn get_concretized_keys_user_transaction(
        &self,
        tx: &SignedTransaction,
        blockchain_view: &impl MoveResolver,
    ) -> Result<(Vec<ResourceKey>, Vec<ResourceKey>)> {
        match tx.payload() {
            TransactionPayload::ScriptFunction(s) => {
                let signers = vec![tx.sender()];
                let gas_currency = account_config::type_tag_for_currency_code(
                    account_config::from_currency_code_string(tx.gas_currency_code())?,
                );
                let prologue_accesses = self.get_concretized_summary(
                    &account_config::constants::ACCOUNT_MODULE,
                    SCRIPT_PROLOGUE_NAME,
                    &signers,
                    &serialize_values(&vec![
                        MoveValue::U64(tx.sequence_number()),
                        MoveValue::vector_u8(tx.authenticator().sender().public_key_bytes()),
                        MoveValue::U64(tx.gas_unit_price()),
                        MoveValue::U64(tx.max_gas_amount()),
                        MoveValue::U64(tx.expiration_timestamp_secs()),
                        MoveValue::U8(tx.chain_id().id()),
                        MoveValue::vector_u8(vec![]), // script_hash; it's ignored
                    ]),
                    &[gas_currency.clone()],
                    blockchain_view,
                )?;
                let epilogue_accesses = self.get_concretized_summary(
                    &account_config::constants::ACCOUNT_MODULE,
                    USER_EPILOGUE_NAME,
                    &signers,
                    &serialize_values(&vec![
                        MoveValue::U64(tx.sequence_number()),
                        MoveValue::U64(tx.gas_unit_price()),
                        MoveValue::U64(tx.max_gas_amount()),
                        MoveValue::U64(0), // gas_units_remaining
                    ]),
                    &[gas_currency.clone()],
                    blockchain_view,
                )?;
                let script_accesses = self.get_concretized_summary(
                    s.module(),
                    s.function(),
                    &signers,
                    s.args(),
                    s.ty_args(),
                    blockchain_view,
                )?;

                let mut keys_read = vec![];
                let mut keys_written = vec![];

                keys_read.extend(
                    prologue_accesses
                        .get_keys_read()
                        .ok_or_else(|| anyhow!("Failed to get read set for prologue"))?,
                );
                keys_read.extend(
                    epilogue_accesses
                        .get_keys_read()
                        .ok_or_else(|| anyhow!("Failed to get read set for epilogue"))?,
                );
                keys_read.extend(
                    script_accesses
                        .get_keys_read()
                        .ok_or_else(|| anyhow!("Failed to get read set for script body"))?,
                );

                keys_written.extend(
                    prologue_accesses
                        .get_keys_written()
                        .ok_or_else(|| anyhow!("Failed to get read set for prologue"))?,
                );
                keys_written.extend(
                    epilogue_accesses
                        .get_keys_written()
                        .ok_or_else(|| anyhow!("Failed to get read set for epilogue"))?,
                );
                keys_written.extend(
                    script_accesses
                        .get_keys_written()
                        .ok_or_else(|| anyhow!("Failed to get read set for script body"))?,
                );

                keys_read.sort();
                keys_read.dedup();

                keys_written.sort();
                keys_written.dedup();
                // Hack: remove GasFees accesses from epilogue if gas_price is zero. This is sound
                // to do as of Diem 1.3, but should be re-evaluated if the epilogue changes
                if tx.gas_unit_price() == 0 {
                    let tx_fees_tag = StructTag {
                        address: account_config::CORE_CODE_ADDRESS,
                        module: TRANSACTION_FEES_NAME.to_owned(),
                        name: TRANSACTION_FEES_NAME.to_owned(),
                        type_params: vec![gas_currency],
                    };
                    keys_written.retain(|r| r.type_() != &tx_fees_tag);
                }

                Ok((keys_read, keys_written))
            }
            payload => {
                // TODO: support tx scripts here. Slightly tricky since we will need to run
                // analyzer on the fly
                bail!("Unsupported transaction payload type {:?}", payload)
            }
        }
    }

    pub fn get_concretized_keys_tx(
        &self,
        tx: &PreprocessedTransaction,
        blockchain_view: &impl MoveResolver,
    ) -> Result<(Vec<ResourceKey>, Vec<ResourceKey>)> {
        match tx {
            PreprocessedTransaction::UserTransaction(tx) => {
                self.get_concretized_keys_user_transaction(tx, blockchain_view)
            }
            PreprocessedTransaction::BlockMetadata(block_metadata) => {
                let (round, timestamp, previous_vote, proposer) =
                    block_metadata.clone().into_inner();
                let args = serialize_values(&vec![
                    MoveValue::Signer(account_config::reserved_vm_address()),
                    MoveValue::U64(round),
                    MoveValue::U64(timestamp),
                    MoveValue::Vector(previous_vote.into_iter().map(MoveValue::Address).collect()),
                    MoveValue::Address(proposer),
                ]);
                let metadata_access = self.get_concretized_summary(
                    &DIEM_BLOCK_MODULE,
                    BLOCK_PROLOGUE,
                    &[],
                    &args,
                    &[],
                    blockchain_view,
                )?;
                Ok((
                    metadata_access
                        .get_keys_read()
                        .ok_or_else(|| anyhow!("Failed to get read set for block prologue"))?,
                    metadata_access
                        .get_keys_written()
                        .ok_or_else(|| anyhow!("Failed to get read set for block prologue"))?,
                ))
            }
            PreprocessedTransaction::InvalidSignature => Ok((vec![], vec![])),
            PreprocessedTransaction::WriteSet(_) | PreprocessedTransaction::WaypointWriteSet(_) => {
                bail!("Unsupported writeset transaction")
            }
        }
    }
}

impl Deref for ReadWriteSetAnalysis {
    type Target = NormalizedReadWriteSetAnalysis;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![allow(unstable)]
#[no_link] #[plugin] #[macro_use]
extern crate jit_macros;
extern crate jit;
extern crate test;
use jit::*;
use std::default::Default;
macro_rules! test_compile(
    ($ty:ty, $test_name:ident, $kind:expr) => (
        #[test]
        fn $test_name() {
            let default_value:$ty = Default::default();
            let mut ctx = Context::new();
            jit_func!(ctx, func, gen_value() -> $ty, {
                let val = func.insn_of(&default_value);
                func.insn_return(val);
            }, |func| {
                assert_eq!(func(()), default_value);
            });
        }
    );
);
test_compile!((), test_compile_void, Void);
test_compile!(f64, test_compile_f64, Float64);
test_compile!(f32, test_compile_f32, Float32);
test_compile!(isize, test_compile_isize, NInt);
test_compile!(usize, test_compile_usize, NUInt);
test_compile!(i32, test_compile_i32, Int);
test_compile!(u32, test_compile_u32, UInt);
test_compile!(i16, test_compile_i16, Short);
test_compile!(u16, test_compile_u16, UShort);
test_compile!(i8, test_compile_i8, SByte);
test_compile!(u8, test_compile_u8, UByte);<commit_msg>Add an assertion for checking the static type too<commit_after>#![feature(plugin)]
#![allow(unstable)]
#[no_link] #[plugin] #[macro_use]
extern crate jit_macros;
extern crate jit;
extern crate test;
use jit::*;
use std::default::Default;
macro_rules! test_compile(
    ($ty:ty, $test_name:ident, $id:ident, $kind:expr) => (
        #[test]
        fn $test_name() {
            let default_value:$ty = Default::default();
            assert!(typecs::$id.get() == get::<$ty>());
            let mut ctx = Context::new();
            jit_func!(ctx, func, gen_value() -> $ty, {
                let val = func.insn_of(&default_value);
                func.insn_return(val);
            }, |func| {
                assert_eq!(func(()), default_value);
            });
        }
    );
);
test_compile!(f64, test_compile_f64, FLOAT64, Float64);
test_compile!(f32, test_compile_f32, FLOAT32, Float32);
test_compile!(isize, test_compile_isize, NINT, NInt);
test_compile!(usize, test_compile_usize, NUINT, NUInt);
test_compile!(i64, test_compile_long, LONG, Long);
test_compile!(u64, test_compile_ulong, ULONG,  ULong);
test_compile!(i32, test_compile_i32, INT, Int);
test_compile!(u32, test_compile_u32, UINT, UInt);
test_compile!(i16, test_compile_i16, SHORT, Short);
test_compile!(u16, test_compile_u16, USHORT, UShort);
test_compile!(i8, test_compile_i8, SBYTE, SByte);
test_compile!(u8, test_compile_u8, UBYTE,  UByte);<|endoftext|>


<commit_before>#[test]
fn dogfood() {
    if option_env!("RUSTC_TEST_SUITE").is_some() {
        return;
    }
    let root_dir = std::env::current_dir().unwrap();
    for d in &[".", "clippy_lints"] {
        std::env::set_current_dir(root_dir.join(d)).unwrap();
        let output = std::process::Command::new("cargo")
            .arg("run")
            .arg("--bin")
            .arg("cargo-clippy")
            .arg("--manifest-path")
            .arg(root_dir.join("Cargo.toml"))
            .env("CLIPPY_DOGFOOD", "true")
            .output()
            .unwrap();
        println!("status: {}", output.status);
        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

        assert!(output.status.success());
    }
}
<commit_msg>tests: dogfood: extend to run with --all-features and clippy::internal enabled. Run it on rustc_tools_util and clippy_dev as well.<commit_after>#[test]
fn dogfood() {
    if option_env!("RUSTC_TEST_SUITE").is_some() {
        return;
    }
    let root_dir = std::env::current_dir().unwrap();
    for d in &[".", "clippy_lints", "rustc_tools_util", "clippy_dev"] {
        std::env::set_current_dir(root_dir.join(d)).unwrap();
        let output = std::process::Command::new("cargo")
            .arg("run")
            .arg("--bin")
            .arg("cargo-clippy")
            .arg("--all-features")
            .arg("--manifest-path")
            .arg(root_dir.join("Cargo.toml"))
            .args(&["--", "-W clippy::internal"])
            .env("CLIPPY_DOGFOOD", "true")
            .output()
            .unwrap();
        println!("status: {}", output.status);
        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

        assert!(output.status.success());
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! This module defines the Exonum services interfaces. Like smart contracts in some other
//! blockchain platforms, Exonum services encapsulate business logic of the blockchain application.

use std::fmt;
use std::sync::{Arc, RwLock};
use std::collections::{HashMap, HashSet};
use std::net::SocketAddr;

use serde_json::Value;
use iron::Handler;

use crypto::{Hash, PublicKey, SecretKey};
use storage::{Fork, Snapshot};
use messages::{Message, RawTransaction};
use encoding::Error as MessageError;
use node::{ApiSender, Node, State, TransactionSend};
use blockchain::{Blockchain, ConsensusConfig, Schema, StoredConfiguration, ValidatorKeys};
use helpers::{Height, Milliseconds, ValidatorId};

/// A trait that describes transaction processing rules (a group of sequential operations
/// with the Exonum storage) for the given `Message`.
pub trait Transaction: Message + 'static {
    /// Verifies the transaction, which includes the message signature verification and other
    /// specific internal constraints. verify is intended to check the internal consistency of
    /// a transaction; it has no access to the blockchain state.
    /// If a transaction fails verify, it is considered incorrect and cannot be included into
    /// any correct block proposal. Incorrect transactions are never included into the blockchain.
    ///
    /// *This method should not use external data, that is, it must be a pure function.*
    fn verify(&self) -> bool;
    /// Takes the current blockchain state via `fork` and can modify it if certain conditions
    /// are met.
    ///
    /// # Notes
    ///
    /// - When programming `execute`, you should perform state-related checks before any changes
    /// to the state and return early if these checks fail.
    /// - If the execute method of a transaction raises a `panic`, the changes made by the
    /// transactions are discarded, but the transaction itself is still considered committed.
    fn execute(&self, fork: &mut Fork);
    /// Returns the useful information about the transaction in the JSON format. The returned value
    /// is used to fill the [`TxInfo.content`] field in [the blockchain explorer][explorer].
    ///
    /// # Notes
    ///
    /// The default implementation returns `null`. For transactions defined with
    /// the [`message!`] macro, you may redefine `info()` as
    ///
    /// ```
    /// # #[macro_use] extern crate exonum;
    /// extern crate serde_json;
    /// # use exonum::blockchain::Transaction;
    /// # use exonum::storage::Fork;
    ///
    /// message! {
    ///     struct MyTransaction {
    ///         // Transaction definition...
    /// #       const TYPE = 1;
    /// #       const ID = 1;
    /// #       const SIZE = 8;
    /// #       field foo: u64 [0 => 8]
    ///     }
    /// }
    ///
    /// impl Transaction for MyTransaction {
    ///     // Other methods...
    /// #   fn verify(&self) -> bool { true }
    /// #   fn execute(&self, _: &mut Fork) { }
    ///
    ///     fn info(&self) -> serde_json::Value {
    ///         serde_json::to_value(self).expect("Cannot serialize transaction to JSON")
    ///     }
    /// }
    /// # fn main() { }
    /// ```
    ///
    /// [`TxInfo.content`]: ../explorer/struct.TxInfo.html#structfield.content
    /// [explorer]: ../explorer/index.html
    /// [`message!`]: ../macro.message.html
    fn info(&self) -> Value {
        Value::Null
    }
}

/// A trait that describes a business-logic of the concrete service.
#[allow(unused_variables, unused_mut)]
pub trait Service: Send + Sync + 'static {
    /// Unique service identification for database schema and service messages.
    fn service_id(&self) -> u16;

    /// Unique human readable service name.
    fn service_name(&self) -> &'static str;

    /// Returns a list of root hashes of tables that determine the current state
    /// of the service database. These hashes are collected from all services in a common
    ///  `MerklePatriciaTable` that named [`state_hash_aggregator`][1].
    ///
    /// Empty `Vec` can be returned if service don't want to change blockchain state.
    ///
    /// See also [`service_table_unique_key`][2].
    ///
    /// [1]: struct.Schema.html#method.state_hash_aggregator
    /// [2]: struct.Blockchain.html#method.service_table_unique_key
    fn state_hash(&self, snapshot: &Snapshot) -> Vec<Hash>;

    /// Tries to create `Transaction` object from the given raw message.
    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, MessageError>;

    /// By this method you can initialize information schema of service
    /// and generates initial service configuration.
    /// This method is called on genesis block creation event.
    fn initialize(&self, fork: &mut Fork) -> Value {
        Value::Null
    }

    /// Handles commit event. This handler is invoked for each service after commit of the block.
    /// For example service can create some transaction if the specific condition occurred.
    ///
    /// *Try not to perform long operations here*.
    fn handle_commit(&self, context: &ServiceContext) {}

    /// Returns api handler for public users.
    fn public_api_handler(&self, context: &ApiContext) -> Option<Box<Handler>> {
        None
    }

    /// Returns api handler for maintainers.
    fn private_api_handler(&self, context: &ApiContext) -> Option<Box<Handler>> {
        None
    }
}

/// The current node state on which the blockchain is running, or in other words
/// execution context.
#[derive(Debug)]
pub struct ServiceContext {
    validator_id: Option<ValidatorId>,
    service_keypair: (PublicKey, SecretKey),
    api_sender: ApiSender,
    fork: Fork,
    stored_configuration: StoredConfiguration,
    height: Height,
}

impl ServiceContext {
    /// Creates the service context for the given node.
    ///
    /// This method is necessary if you want to implement an alternative exonum node.
    /// For example, you can implement special node without consensus for regression
    /// testing of services business logic.
    pub fn new(
        service_public_key: PublicKey,
        service_secret_key: SecretKey,
        api_sender: ApiSender,
        fork: Fork,
    ) -> ServiceContext {
        let (stored_configuration, height) = {
            let schema = Schema::new(fork.as_ref());
            let stored_configuration = schema.actual_configuration();
            let height = schema.height();
            (stored_configuration, height)
        };
        let validator_id = stored_configuration
            .validator_keys
            .iter()
            .position(|validator| service_public_key == validator.service_key)
            .map(|id| ValidatorId(id as u16));

        ServiceContext {
            validator_id,
            service_keypair: (service_public_key, service_secret_key),
            api_sender,
            fork,
            stored_configuration,
            height,
        }
    }

    /// If the current node is validator returns its identifier.
    /// For other nodes return `None`.
    pub fn validator_id(&self) -> Option<ValidatorId> {
        self.validator_id
    }

    /// Returns the current database snapshot.
    pub fn snapshot(&self) -> &Snapshot {
        self.fork.as_ref()
    }

    /// Returns the current blockchain height. This height is "height of the last committed block".
    pub fn height(&self) -> Height {
        self.height
    }

    /// Returns the current list of validators.
    pub fn validators(&self) -> &[ValidatorKeys] {
        self.stored_configuration.validator_keys.as_slice()
    }

    /// Returns current node's public key.
    pub fn public_key(&self) -> &PublicKey {
        &self.service_keypair.0
    }

    /// Returns current node's secret key.
    pub fn secret_key(&self) -> &SecretKey {
        &self.service_keypair.1
    }

    /// Returns the actual consensus configuration.
    pub fn actual_consensus_config(&self) -> &ConsensusConfig {
        &self.stored_configuration.consensus
    }

    /// Returns service specific global variables as json value.
    pub fn actual_service_config(&self, service: &Service) -> &Value {
        &self.stored_configuration.services[service.service_name()]
    }

    /// Returns reference to the transaction sender.
    pub fn transaction_sender(&self) -> &TransactionSend {
        &self.api_sender
    }

    /// Returns the actual blockchain global configuration.
    pub fn stored_configuration(&self) -> &StoredConfiguration {
        &self.stored_configuration
    }
}

#[derive(Debug, Default)]
pub struct ApiNodeState {
    incoming_connections: HashSet<SocketAddr>,
    outgoing_connections: HashSet<SocketAddr>,
    reconnects_timeout: HashMap<SocketAddr, Milliseconds>,
    //TODO: update on event?
    peers_info: HashMap<SocketAddr, PublicKey>,
}

impl ApiNodeState {
    fn new() -> ApiNodeState {
        Self::default()
    }
}

/// Shared part of the context, used to take some values from the `Node`s `State`
/// should be used to take some metrics.
#[derive(Clone, Debug)]
pub struct SharedNodeState {
    state: Arc<RwLock<ApiNodeState>>,
    /// Timeout to update api state.
    pub state_update_timeout: Milliseconds,
}

impl SharedNodeState {
    /// Creates new `SharedNodeState`
    pub fn new(state_update_timeout: Milliseconds) -> SharedNodeState {
        SharedNodeState {
            state: Arc::new(RwLock::new(ApiNodeState::new())),
            state_update_timeout,
        }
    }
    /// Return list of connected sockets
    pub fn incoming_connections(&self) -> Vec<SocketAddr> {
        self.state
            .read()
            .expect("Expected read lock.")
            .incoming_connections
            .iter()
            .cloned()
            .collect()
    }
    /// Return list of our connection sockets
    pub fn outgoing_connections(&self) -> Vec<SocketAddr> {
        self.state
            .read()
            .expect("Expected read lock.")
            .outgoing_connections
            .iter()
            .cloned()
            .collect()
    }
    /// Return reconnects list
    pub fn reconnects_timeout(&self) -> Vec<(SocketAddr, Milliseconds)> {
        self.state
            .read()
            .expect("Expected read lock.")
            .reconnects_timeout
            .iter()
            .map(|(c, e)| (*c, *e))
            .collect()
    }
    /// Return peers info list
    pub fn peers_info(&self) -> Vec<(SocketAddr, PublicKey)> {
        self.state
            .read()
            .expect("Expected read lock.")
            .peers_info
            .iter()
            .map(|(c, e)| (*c, *e))
            .collect()
    }
    /// Update internal state, from `Node` State`
    pub fn update_node_state(&self, state: &State) {
        for (p, c) in state.peers().iter() {
            self.state
                .write()
                .expect("Expected write lock.")
                .peers_info
                .insert(c.addr(), *p);
        }
    }

    /// Returns value of the `state_update_timeout`.
    pub fn state_update_timeout(&self) -> Milliseconds {
        self.state_update_timeout
    }

    /// add incomming connection into state
    pub fn add_incoming_connection(&self, addr: SocketAddr) {
        self.state
            .write()
            .expect("Expected write lock")
            .incoming_connections
            .insert(addr);
    }
    /// add outgoing connection into state
    pub fn add_outgoing_connection(&self, addr: SocketAddr) {
        self.state
            .write()
            .expect("Expected write lock")
            .outgoing_connections
            .insert(addr);
    }

    /// remove incomming connection from state
    pub fn remove_incoming_connection(&self, addr: &SocketAddr) -> bool {
        self.state
            .write()
            .expect("Expected write lock")
            .incoming_connections
            .remove(addr)
    }

    /// remove outgoing connection from state
    pub fn remove_outgoing_connection(&self, addr: &SocketAddr) -> bool {
        self.state
            .write()
            .expect("Expected write lock")
            .outgoing_connections
            .remove(addr)
    }

    /// Add reconect timeout
    pub fn add_reconnect_timeout(
        &self,
        addr: SocketAddr,
        timeout: Milliseconds,
    ) -> Option<Milliseconds> {
        self.state
            .write()
            .expect("Expected write lock")
            .reconnects_timeout
            .insert(addr, timeout)
    }

    /// Removes reconect timeout and returns the previous value.
    pub fn remove_reconnect_timeout(&self, addr: &SocketAddr) -> Option<Milliseconds> {
        self.state
            .write()
            .expect("Expected write lock")
            .reconnects_timeout
            .remove(addr)
    }
}

/// Provides the current node state to api handlers.
pub struct ApiContext {
    blockchain: Blockchain,
    node_channel: ApiSender,
    public_key: PublicKey,
    secret_key: SecretKey,
}

/// Provides the current node state to api handlers.
impl ApiContext {
    /// Constructs context for the given `Node`.
    pub fn new(node: &Node) -> ApiContext {
        let handler = node.handler();
        ApiContext {
            blockchain: handler.blockchain.clone(),
            node_channel: node.channel(),
            public_key: *node.state().service_public_key(),
            secret_key: node.state().service_secret_key().clone(),
        }
    }

    /// Constructs context from raw parts.
    pub fn from_parts(
        blockchain: &Blockchain,
        node_channel: ApiSender,
        public_key: &PublicKey,
        secret_key: &SecretKey,
    ) -> ApiContext {
        ApiContext {
            blockchain: blockchain.clone(),
            node_channel,
            public_key: *public_key,
            secret_key: secret_key.clone(),
        }
    }

    /// Returns reference to the node's blockchain.
    pub fn blockchain(&self) -> &Blockchain {
        &self.blockchain
    }

    /// Returns reference to the transaction sender.
    pub fn node_channel(&self) -> &ApiSender {
        &self.node_channel
    }

    /// Returns the public key of current node.
    pub fn public_key(&self) -> &PublicKey {
        &self.public_key
    }

    /// Returns the secret key of current node.
    pub fn secret_key(&self) -> &SecretKey {
        &self.secret_key
    }
}

impl ::std::fmt::Debug for ApiContext {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "ApiContext(blockchain: {:?}, public_key: {:?})",
            self.blockchain,
            self.public_key
        )
    }
}

impl<'a, S: Service> From<S> for Box<Service + 'a> {
    fn from(s: S) -> Self {
        Box::new(s) as Box<Service>
    }
}

impl<'a, T: Transaction> From<T> for Box<Transaction + 'a> {
    fn from(tx: T) -> Self {
        Box::new(tx) as Box<Transaction>
    }
}
<commit_msg>Add example(s)<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! This module defines the Exonum services interfaces. Like smart contracts in some other
//! blockchain platforms, Exonum services encapsulate business logic of the blockchain application.

use std::fmt;
use std::sync::{Arc, RwLock};
use std::collections::{HashMap, HashSet};
use std::net::SocketAddr;

use serde_json::Value;
use iron::Handler;

use crypto::{Hash, PublicKey, SecretKey};
use storage::{Fork, Snapshot};
use messages::{Message, RawTransaction};
use encoding::Error as MessageError;
use node::{ApiSender, Node, State, TransactionSend};
use blockchain::{Blockchain, ConsensusConfig, Schema, StoredConfiguration, ValidatorKeys};
use helpers::{Height, Milliseconds, ValidatorId};

/// A trait that describes transaction processing rules (a group of sequential operations
/// with the Exonum storage) for the given `Message`.
pub trait Transaction: Message + 'static {
    /// Verifies the transaction, which includes the message signature verification and other
    /// specific internal constraints. verify is intended to check the internal consistency of
    /// a transaction; it has no access to the blockchain state.
    /// If a transaction fails verify, it is considered incorrect and cannot be included into
    /// any correct block proposal. Incorrect transactions are never included into the blockchain.
    ///
    /// *This method should not use external data, that is, it must be a pure function.*
    fn verify(&self) -> bool;
    /// Takes the current blockchain state via `fork` and can modify it if certain conditions
    /// are met.
    ///
    /// # Notes
    ///
    /// - When programming `execute`, you should perform state-related checks before any changes
    /// to the state and return early if these checks fail.
    /// - If the execute method of a transaction raises a `panic`, the changes made by the
    /// transactions are discarded, but the transaction itself is still considered committed.
    fn execute(&self, fork: &mut Fork);
    /// Returns the useful information about the transaction in the JSON format. The returned value
    /// is used to fill the [`TxInfo.content`] field in [the blockchain explorer][explorer].
    ///
    /// # Notes
    ///
    /// The default implementation returns `null`. For transactions defined with
    /// the [`message!`] macro, you may redefine `info()` as
    ///
    /// ```
    /// # #[macro_use] extern crate exonum;
    /// extern crate serde_json;
    /// # use exonum::blockchain::Transaction;
    /// # use exonum::storage::Fork;
    ///
    /// message! {
    ///     struct MyTransaction {
    ///         // Transaction definition...
    /// #       const TYPE = 1;
    /// #       const ID = 1;
    /// #       const SIZE = 8;
    /// #       field foo: u64 [0 => 8]
    ///     }
    /// }
    ///
    /// impl Transaction for MyTransaction {
    ///     // Other methods...
    /// #   fn verify(&self) -> bool { true }
    /// #   fn execute(&self, _: &mut Fork) { }
    ///
    ///     fn info(&self) -> serde_json::Value {
    ///         serde_json::to_value(self).expect("Cannot serialize transaction to JSON")
    ///     }
    /// }
    /// # fn main() { }
    /// ```
    ///
    /// [`TxInfo.content`]: ../explorer/struct.TxInfo.html#structfield.content
    /// [explorer]: ../explorer/index.html
    /// [`message!`]: ../macro.message.html
    fn info(&self) -> Value {
        Value::Null
    }
}

/// A trait that describes a business-logic of the concrete service.
#[allow(unused_variables, unused_mut)]
pub trait Service: Send + Sync + 'static {
    /// Unique service identification for database schema and service messages.
    fn service_id(&self) -> u16;

    /// Unique human readable service name.
    ///
    /// # Examples
    ///
    /// ```
    /// use exonum::blockchain::Service;
    /// use exonum::crypto::Hash;
    /// # use exonum::blockchain::Transaction;
    /// # use exonum::messages::RawTransaction;
    /// # use exonum::encoding::Error as MessageError;
    /// # use exonum::storage::Snapshot;
    ///
    /// struct MyService {}
    ///
    /// impl Service for MyService {
    /// #   fn service_id(&self) -> u16 {
    /// #       8000
    /// #   }
    ///     fn service_name(&self) -> &'static str {
    ///         "my_special_unique_service"
    ///     }
    /// #   fn state_hash(&self, _: &Snapshot) -> Vec<Hash> {
    /// #       Vec::new()
    /// #   }
    /// #   fn tx_from_raw(&self, _: RawTransaction) -> Result<Box<Transaction>, MessageError> {
    /// #       unimplemented!()
    /// #   }
    /// }
    /// ```
    fn service_name(&self) -> &'static str;

    /// Returns a list of root hashes of tables that determine the current state
    /// of the service database. These hashes are collected from all services in a common
    ///  `MerklePatriciaTable` that named [`state_hash_aggregator`][1].
    ///
    /// Empty `Vec` can be returned if service don't want to change blockchain state.
    ///
    /// See also [`service_table_unique_key`][2].
    ///
    /// [1]: struct.Schema.html#method.state_hash_aggregator
    /// [2]: struct.Blockchain.html#method.service_table_unique_key
    ///
    /// # Examples
    ///
    /// ```
    /// use exonum::blockchain::Service;
    /// use exonum::crypto::Hash;
    /// use exonum::storage::Snapshot;
    /// # use exonum::blockchain::Transaction;
    /// # use exonum::messages::RawTransaction;
    /// # use exonum::encoding::Error as MessageError;
    ///
    /// struct MyService {}
    ///
    /// impl Service for MyService {
    /// #   fn service_id(&self) -> u16 {
    /// #       8000
    /// #   }
    /// #   fn service_name(&self) -> &'static str {
    /// #       "my_special_unique_service"
    /// #   }
    ///     fn state_hash(&self, _: &Snapshot) -> Vec<Hash> {
    ///         Vec::new()
    ///     }
    /// #   fn tx_from_raw(&self, _: RawTransaction) -> Result<Box<Transaction>, MessageError> {
    /// #       unimplemented!()
    /// #   }
    /// }
    /// ```
    fn state_hash(&self, snapshot: &Snapshot) -> Vec<Hash>;

    /// Tries to create `Transaction` object from the given raw message.
    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, MessageError>;

    /// By this method you can initialize information schema of service
    /// and generates initial service configuration.
    /// This method is called on genesis block creation event.
    fn initialize(&self, fork: &mut Fork) -> Value {
        Value::Null
    }

    /// Handles commit event. This handler is invoked for each service after commit of the block.
    /// For example service can create some transaction if the specific condition occurred.
    ///
    /// *Try not to perform long operations here*.
    fn handle_commit(&self, context: &ServiceContext) {}

    /// Returns api handler for public users.
    fn public_api_handler(&self, context: &ApiContext) -> Option<Box<Handler>> {
        None
    }

    /// Returns api handler for maintainers.
    fn private_api_handler(&self, context: &ApiContext) -> Option<Box<Handler>> {
        None
    }
}

/// The current node state on which the blockchain is running, or in other words
/// execution context.
#[derive(Debug)]
pub struct ServiceContext {
    validator_id: Option<ValidatorId>,
    service_keypair: (PublicKey, SecretKey),
    api_sender: ApiSender,
    fork: Fork,
    stored_configuration: StoredConfiguration,
    height: Height,
}

impl ServiceContext {
    /// Creates the service context for the given node.
    ///
    /// This method is necessary if you want to implement an alternative exonum node.
    /// For example, you can implement special node without consensus for regression
    /// testing of services business logic.
    pub fn new(
        service_public_key: PublicKey,
        service_secret_key: SecretKey,
        api_sender: ApiSender,
        fork: Fork,
    ) -> ServiceContext {
        let (stored_configuration, height) = {
            let schema = Schema::new(fork.as_ref());
            let stored_configuration = schema.actual_configuration();
            let height = schema.height();
            (stored_configuration, height)
        };
        let validator_id = stored_configuration
            .validator_keys
            .iter()
            .position(|validator| service_public_key == validator.service_key)
            .map(|id| ValidatorId(id as u16));

        ServiceContext {
            validator_id,
            service_keypair: (service_public_key, service_secret_key),
            api_sender,
            fork,
            stored_configuration,
            height,
        }
    }

    /// If the current node is validator returns its identifier.
    /// For other nodes return `None`.
    pub fn validator_id(&self) -> Option<ValidatorId> {
        self.validator_id
    }

    /// Returns the current database snapshot.
    pub fn snapshot(&self) -> &Snapshot {
        self.fork.as_ref()
    }

    /// Returns the current blockchain height. This height is "height of the last committed block".
    pub fn height(&self) -> Height {
        self.height
    }

    /// Returns the current list of validators.
    pub fn validators(&self) -> &[ValidatorKeys] {
        self.stored_configuration.validator_keys.as_slice()
    }

    /// Returns current node's public key.
    pub fn public_key(&self) -> &PublicKey {
        &self.service_keypair.0
    }

    /// Returns current node's secret key.
    pub fn secret_key(&self) -> &SecretKey {
        &self.service_keypair.1
    }

    /// Returns the actual consensus configuration.
    pub fn actual_consensus_config(&self) -> &ConsensusConfig {
        &self.stored_configuration.consensus
    }

    /// Returns service specific global variables as json value.
    pub fn actual_service_config(&self, service: &Service) -> &Value {
        &self.stored_configuration.services[service.service_name()]
    }

    /// Returns reference to the transaction sender.
    pub fn transaction_sender(&self) -> &TransactionSend {
        &self.api_sender
    }

    /// Returns the actual blockchain global configuration.
    pub fn stored_configuration(&self) -> &StoredConfiguration {
        &self.stored_configuration
    }
}

#[derive(Debug, Default)]
pub struct ApiNodeState {
    incoming_connections: HashSet<SocketAddr>,
    outgoing_connections: HashSet<SocketAddr>,
    reconnects_timeout: HashMap<SocketAddr, Milliseconds>,
    //TODO: update on event?
    peers_info: HashMap<SocketAddr, PublicKey>,
}

impl ApiNodeState {
    fn new() -> ApiNodeState {
        Self::default()
    }
}

/// Shared part of the context, used to take some values from the `Node`s `State`
/// should be used to take some metrics.
#[derive(Clone, Debug)]
pub struct SharedNodeState {
    state: Arc<RwLock<ApiNodeState>>,
    /// Timeout to update api state.
    pub state_update_timeout: Milliseconds,
}

impl SharedNodeState {
    /// Creates new `SharedNodeState`
    pub fn new(state_update_timeout: Milliseconds) -> SharedNodeState {
        SharedNodeState {
            state: Arc::new(RwLock::new(ApiNodeState::new())),
            state_update_timeout,
        }
    }
    /// Return list of connected sockets
    pub fn incoming_connections(&self) -> Vec<SocketAddr> {
        self.state
            .read()
            .expect("Expected read lock.")
            .incoming_connections
            .iter()
            .cloned()
            .collect()
    }
    /// Return list of our connection sockets
    pub fn outgoing_connections(&self) -> Vec<SocketAddr> {
        self.state
            .read()
            .expect("Expected read lock.")
            .outgoing_connections
            .iter()
            .cloned()
            .collect()
    }
    /// Return reconnects list
    pub fn reconnects_timeout(&self) -> Vec<(SocketAddr, Milliseconds)> {
        self.state
            .read()
            .expect("Expected read lock.")
            .reconnects_timeout
            .iter()
            .map(|(c, e)| (*c, *e))
            .collect()
    }
    /// Return peers info list
    pub fn peers_info(&self) -> Vec<(SocketAddr, PublicKey)> {
        self.state
            .read()
            .expect("Expected read lock.")
            .peers_info
            .iter()
            .map(|(c, e)| (*c, *e))
            .collect()
    }
    /// Update internal state, from `Node` State`
    pub fn update_node_state(&self, state: &State) {
        for (p, c) in state.peers().iter() {
            self.state
                .write()
                .expect("Expected write lock.")
                .peers_info
                .insert(c.addr(), *p);
        }
    }

    /// Returns value of the `state_update_timeout`.
    pub fn state_update_timeout(&self) -> Milliseconds {
        self.state_update_timeout
    }

    /// add incomming connection into state
    pub fn add_incoming_connection(&self, addr: SocketAddr) {
        self.state
            .write()
            .expect("Expected write lock")
            .incoming_connections
            .insert(addr);
    }
    /// add outgoing connection into state
    pub fn add_outgoing_connection(&self, addr: SocketAddr) {
        self.state
            .write()
            .expect("Expected write lock")
            .outgoing_connections
            .insert(addr);
    }

    /// remove incomming connection from state
    pub fn remove_incoming_connection(&self, addr: &SocketAddr) -> bool {
        self.state
            .write()
            .expect("Expected write lock")
            .incoming_connections
            .remove(addr)
    }

    /// remove outgoing connection from state
    pub fn remove_outgoing_connection(&self, addr: &SocketAddr) -> bool {
        self.state
            .write()
            .expect("Expected write lock")
            .outgoing_connections
            .remove(addr)
    }

    /// Add reconect timeout
    pub fn add_reconnect_timeout(
        &self,
        addr: SocketAddr,
        timeout: Milliseconds,
    ) -> Option<Milliseconds> {
        self.state
            .write()
            .expect("Expected write lock")
            .reconnects_timeout
            .insert(addr, timeout)
    }

    /// Removes reconect timeout and returns the previous value.
    pub fn remove_reconnect_timeout(&self, addr: &SocketAddr) -> Option<Milliseconds> {
        self.state
            .write()
            .expect("Expected write lock")
            .reconnects_timeout
            .remove(addr)
    }
}

/// Provides the current node state to api handlers.
pub struct ApiContext {
    blockchain: Blockchain,
    node_channel: ApiSender,
    public_key: PublicKey,
    secret_key: SecretKey,
}

/// Provides the current node state to api handlers.
impl ApiContext {
    /// Constructs context for the given `Node`.
    pub fn new(node: &Node) -> ApiContext {
        let handler = node.handler();
        ApiContext {
            blockchain: handler.blockchain.clone(),
            node_channel: node.channel(),
            public_key: *node.state().service_public_key(),
            secret_key: node.state().service_secret_key().clone(),
        }
    }

    /// Constructs context from raw parts.
    pub fn from_parts(
        blockchain: &Blockchain,
        node_channel: ApiSender,
        public_key: &PublicKey,
        secret_key: &SecretKey,
    ) -> ApiContext {
        ApiContext {
            blockchain: blockchain.clone(),
            node_channel,
            public_key: *public_key,
            secret_key: secret_key.clone(),
        }
    }

    /// Returns reference to the node's blockchain.
    pub fn blockchain(&self) -> &Blockchain {
        &self.blockchain
    }

    /// Returns reference to the transaction sender.
    pub fn node_channel(&self) -> &ApiSender {
        &self.node_channel
    }

    /// Returns the public key of current node.
    pub fn public_key(&self) -> &PublicKey {
        &self.public_key
    }

    /// Returns the secret key of current node.
    pub fn secret_key(&self) -> &SecretKey {
        &self.secret_key
    }
}

impl ::std::fmt::Debug for ApiContext {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "ApiContext(blockchain: {:?}, public_key: {:?})",
            self.blockchain,
            self.public_key
        )
    }
}

impl<'a, S: Service> From<S> for Box<Service + 'a> {
    fn from(s: S) -> Self {
        Box::new(s) as Box<Service>
    }
}

impl<'a, T: Transaction> From<T> for Box<Transaction + 'a> {
    fn from(tx: T) -> Self {
        Box::new(tx) as Box<Transaction>
    }
}
<|endoftext|>


<commit_before>use ruru::{Object, RString, Symbol};

use ruby::Config as RubyConfig;

use super::BindingType;

pub struct Config {
    binding_type: BindingType,
    listen_on: String,
}

impl Config {
    fn new(binding_type: BindingType, listen_on: String) -> Self {
        Config {
            binding_type: binding_type,
            listen_on: listen_on,
        }
    }

    #[inline]
    fn binding_type(&self) -> &BindingType {
        &self.binding_type
    }

    #[inline]
    fn listen_on(&self) -> &str {
        &self.listen_on
    }
}

impl From<RubyConfig> for Config {
    fn from(ruby_config: RubyConfig) -> Self {
        // TODO: raise an exception if it is not a String
        let listen_on =
            ruby_config
                .send("listen_on", vec![])
                .try_convert_to::<RString>().unwrap()
                .to_string();

        // TODO: raise an exception if it is not a Symbol
        let binding_type =
            ruby_config
                .send("binding_type", vec![])
                .try_convert_to::<Symbol>().unwrap()
                .to_string();

        let binding_type = BindingType::from(binding_type.as_str());

        Config::new(binding_type, listen_on)
    }
}
<commit_msg>Make config::Config methods public<commit_after>use ruru::{Object, RString, Symbol};

use ruby::Config as RubyConfig;

use super::BindingType;

pub struct Config {
    binding_type: BindingType,
    listen_on: String,
}

impl Config {
    pub fn new(binding_type: BindingType, listen_on: String) -> Self {
        Config {
            binding_type: binding_type,
            listen_on: listen_on,
        }
    }

    #[inline]
    pub fn binding_type(&self) -> &BindingType {
        &self.binding_type
    }

    #[inline]
    pub fn listen_on(&self) -> &str {
        &self.listen_on
    }
}

impl From<RubyConfig> for Config {
    fn from(ruby_config: RubyConfig) -> Self {
        // TODO: raise an exception if it is not a String
        let listen_on =
            ruby_config
                .send("listen_on", vec![])
                .try_convert_to::<RString>().unwrap()
                .to_string();

        // TODO: raise an exception if it is not a Symbol
        let binding_type =
            ruby_config
                .send("binding_type", vec![])
                .try_convert_to::<Symbol>().unwrap()
                .to_string();

        let binding_type = BindingType::from(binding_type.as_str());

        Config::new(binding_type, listen_on)
    }
}
<|endoftext|>


<commit_before>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use cpu::common_defs::opcode_class::OpcodeClass;
use memory::Memory;

macro_rules! set_zs {
    ($cpu_state:expr,$val:expr) => (set_z!($cpu_state,$val);set_s!($cpu_state,$val););
}
macro_rules! set_z {
    ($cpu_state:expr,$val:expr) => ($cpu_state.Z = $val == 0;);
}
macro_rules! set_s {
    ($cpu_state:expr,$val:expr) => ($cpu_state.S = $val >= 0x80;);
}
macro_rules! stack_push8 {
    ($cpu_state:expr,$mem:expr,$val:expr) => (try!($mem.write8(0x0100 as u16 | $cpu_state.sp as u16,$val)); $cpu_state.sp-=1;);
}
macro_rules! stack_push16 {
    ($cpu_state:expr,$mem:expr,$val:expr) => (try!($mem.write16(0x0100 as u16 | $cpu_state.sp as u16 -1,$val)); $cpu_state.sp-=2;);
}
macro_rules! stack_pull8 {
    ($cpu_state:expr,$mem:expr) => {
        {
            $cpu_state.sp+=1;
            $mem.read8(0x0100 as u16 | $cpu_state.sp as u16)
        }
    }
}
macro_rules! stack_pull16 {
    ($cpu_state:expr,$mem:expr) => {
        {
            $cpu_state.sp+=2;
            $mem.read16(0x0100 as u16 | $cpu_state.sp as u16 -1)
        }
    }
}
macro_rules! compare {
    ($cpu_state:expr,$a:expr,$b:expr) => {
        {
            set_zs!($cpu_state,$a - $b);
            $cpu_state.C = $a >= $b;
        }
    }
}

#[derive(Debug)]
pub enum ExecutionError {
  MemoryError(::memory::MemoryError),
  UnexpectedOpcode(String),
  UnexpectedAddressMode(String)
}
impl From<::memory::MemoryError> for ExecutionError {
    fn from(err: ::memory::MemoryError) -> ExecutionError {
        ExecutionError::MemoryError(err)
    }
}

pub struct CpuExecutor {
    op_table: Vec<OpcodeExecInfo>,
}

impl CpuExecutor {
	/// Construct a new CpuExecutor.
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        CpuExecutor {
            op_table:  opcodes,
        }
    }

	/// Reset the given cpu_state.
    pub fn power_on(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        self.reset(cpu_state,mem);
    }
	/// Reset the given cpu_state.
    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    /// Execute a single instruction in the given memory and cpu_state context.
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) -> Result<(),ExecutionError> {
        try!(self.fetch_and_decode(cpu_state,mem));
        try!(self.execute(cpu_state,mem));
        Ok(())
    }

	/// Fetch the next instruction and perform address resolution.
    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) -> Result<(),ExecutionError> {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = try!(self.decode(cpu_state,mem));
        cpu_state.pc = cpu_state.pc+1;
        Ok(())
    }

    /// Perform address resolution, returning the info in a DecodeRegister.
    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> Result<DecodeRegister,ExecutionError> {
        let mut dr = DecodeRegister {
            info : self.op_table[cpu_state.instruction_register as usize].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            // no explicit addresses for the following modes
            AddressMode::Accumulator  => { },
            AddressMode::Implied      => { },

            // explicit addresses from here on out
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.x as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteY       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Immediate       => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Indirect        => {
                dr.addr_intermediate = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
            },
            AddressMode::IndexedIndirect => {
                dr.addr_intermediate = Some( (mem.read8(cpu_state.pc+1).unwrap() + cpu_state.x) as u16);
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::IndirectIndexed => {
                dr.addr_init         = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.addr_intermediate = Some(mem.read16(dr.addr_init.unwrap()).unwrap());
                dr.addr_final        = Some( ((dr.addr_intermediate.unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Relative        => {
                dr.value_final = Some(mem.read8(cpu_state.pc+1).unwrap());

                // NOTE: relative is from the end of the current instruction and relative
                // instructions are 2 bytes long, so we add 2 before adding in the specified offset
                //
                if dr.value_final.unwrap() < 0x80 { dr.addr_final  = Some(cpu_state.pc + 2 + dr.value_final.unwrap() as u16); }
                else                              { dr.addr_final  = Some(cpu_state.pc + 2 + dr.value_final.unwrap() as u16 - 0x100);}
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageX       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.x as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageY       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.y as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            _ => { return Err(ExecutionError::UnexpectedAddressMode(format!("unrecognized addressing mode '{:?}' while decoding instruction_register!",dr.info.address_mode))); }
        }

        return Ok(dr);
    }

    /// Perform the current instruction, returning the CpuState after execution.
    pub fn execute(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) -> Result<(),ExecutionError> {
    	// Figure out which opcode is being executed.
    	match cpu_state.decode_register.info.opcode_class {
    		OpcodeClass::ASL => {
				cpu_state.a = cpu_state.a << 1;
				cpu_state.C = (128 & cpu_state.a) > 0;
    			if cpu_state.a == 0 { cpu_state.Z = true; }

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::AND => {
				cpu_state.a = cpu_state.a & cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.a);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::BCC => {
                if !cpu_state.C { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else            { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BCS => {
                if cpu_state.C { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else           { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BEQ => {
                if cpu_state.Z { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else           { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BIT => {
                cpu_state.V = (cpu_state.decode_register.value_final.unwrap() >> 6) & 1 > 0;
                set_z!(cpu_state,cpu_state.decode_register.value_final.unwrap() & cpu_state.a);
                set_s!(cpu_state,cpu_state.decode_register.value_final.unwrap());

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::BNE => {
                if !cpu_state.Z { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else            { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BPL => {
                if !cpu_state.S { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else            { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BVC => {
                if !cpu_state.V { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else            { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BVS => {
                if cpu_state.V { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else           { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::CMP => {
                compare!(cpu_state,cpu_state.a,cpu_state.decode_register.value_final.unwrap());

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::CLC => {
                cpu_state.C = false;
    		},
    		OpcodeClass::CLD => {
                cpu_state.D = false;
    		},
    		OpcodeClass::LDA => {
    			cpu_state.a = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.a);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LDX => {
    			cpu_state.x = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.x);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LDY => {
    			cpu_state.y = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.y);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LSR => {
    			cpu_state.a = cpu_state.a >> 1;
    			cpu_state.C = 1 & cpu_state.a > 0;
    			if cpu_state.a == 0 { cpu_state.Z = true; }

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::JMP => {
    			cpu_state.pc = cpu_state.decode_register.addr_final.unwrap();
    		},
    		OpcodeClass::JSR => {
                stack_push16!(cpu_state,mem,cpu_state.pc+1);
    			cpu_state.pc = cpu_state.decode_register.addr_final.unwrap();
    		},
    		OpcodeClass::NOP => {
            },
    		OpcodeClass::PHA => {
                stack_push8!(cpu_state,mem,cpu_state.a);
            },
            // http://wiki.nesdev.com/w/index.php/Status_flags
    		OpcodeClass::PHP => {
                stack_push8!(cpu_state,mem,cpu_state.unpack_flags() | 0x30);
            },
    		OpcodeClass::PLA => {
                cpu_state.a = stack_pull8!(cpu_state,mem).unwrap();
                set_zs!(cpu_state,cpu_state.a);
            },
    		OpcodeClass::RTS => {
                cpu_state.pc = stack_pull16!(cpu_state,mem).unwrap() + 1;
            },
    		OpcodeClass::SEC => {
                cpu_state.C = true;
    		},
    		OpcodeClass::SED => {
                cpu_state.D = true;
    		},
    		OpcodeClass::SEI => {
                cpu_state.I = true;
    		},
    		OpcodeClass::STA => {
                try!(mem.write8(cpu_state.decode_register.addr_final.unwrap(),cpu_state.a));
                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::STX => {
                try!(mem.write8(cpu_state.decode_register.addr_final.unwrap(),cpu_state.x));
                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},

			_ => { return Err(ExecutionError::UnexpectedOpcode(format!("Unrecognised opcode class: {:?}", cpu_state.decode_register.info.opcode_class)));}

    	}
        Ok(())
    }
}

<commit_msg>implemented PLP opcode<commit_after>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use cpu::common_defs::opcode_class::OpcodeClass;
use memory::Memory;

macro_rules! set_zs {
    ($cpu_state:expr,$val:expr) => (set_z!($cpu_state,$val);set_s!($cpu_state,$val););
}
macro_rules! set_z {
    ($cpu_state:expr,$val:expr) => ($cpu_state.Z = $val == 0;);
}
macro_rules! set_s {
    ($cpu_state:expr,$val:expr) => ($cpu_state.S = $val >= 0x80;);
}
macro_rules! stack_push8 {
    ($cpu_state:expr,$mem:expr,$val:expr) => (try!($mem.write8(0x0100 as u16 | $cpu_state.sp as u16,$val)); $cpu_state.sp-=1;);
}
macro_rules! stack_push16 {
    ($cpu_state:expr,$mem:expr,$val:expr) => (try!($mem.write16(0x0100 as u16 | $cpu_state.sp as u16 -1,$val)); $cpu_state.sp-=2;);
}
macro_rules! stack_pull8 {
    ($cpu_state:expr,$mem:expr) => {
        {
            $cpu_state.sp+=1;
            $mem.read8(0x0100 as u16 | $cpu_state.sp as u16)
        }
    }
}
macro_rules! stack_pull16 {
    ($cpu_state:expr,$mem:expr) => {
        {
            $cpu_state.sp+=2;
            $mem.read16(0x0100 as u16 | $cpu_state.sp as u16 -1)
        }
    }
}
macro_rules! compare {
    ($cpu_state:expr,$a:expr,$b:expr) => {
        {
            set_zs!($cpu_state,$a - $b);
            $cpu_state.C = $a >= $b;
        }
    }
}

#[derive(Debug)]
pub enum ExecutionError {
  MemoryError(::memory::MemoryError),
  UnexpectedOpcode(String),
  UnexpectedAddressMode(String)
}
impl From<::memory::MemoryError> for ExecutionError {
    fn from(err: ::memory::MemoryError) -> ExecutionError {
        ExecutionError::MemoryError(err)
    }
}

pub struct CpuExecutor {
    op_table: Vec<OpcodeExecInfo>,
}

impl CpuExecutor {
	/// Construct a new CpuExecutor.
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        CpuExecutor {
            op_table:  opcodes,
        }
    }

	/// Reset the given cpu_state.
    pub fn power_on(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        self.reset(cpu_state,mem);
    }
	/// Reset the given cpu_state.
    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    /// Execute a single instruction in the given memory and cpu_state context.
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) -> Result<(),ExecutionError> {
        try!(self.fetch_and_decode(cpu_state,mem));
        try!(self.execute(cpu_state,mem));
        Ok(())
    }

	/// Fetch the next instruction and perform address resolution.
    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) -> Result<(),ExecutionError> {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = try!(self.decode(cpu_state,mem));
        cpu_state.pc = cpu_state.pc+1;
        Ok(())
    }

    /// Perform address resolution, returning the info in a DecodeRegister.
    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> Result<DecodeRegister,ExecutionError> {
        let mut dr = DecodeRegister {
            info : self.op_table[cpu_state.instruction_register as usize].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            // no explicit addresses for the following modes
            AddressMode::Accumulator  => { },
            AddressMode::Implied      => { },

            // explicit addresses from here on out
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.x as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteY       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Immediate       => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Indirect        => {
                dr.addr_intermediate = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
            },
            AddressMode::IndexedIndirect => {
                dr.addr_intermediate = Some( (mem.read8(cpu_state.pc+1).unwrap() + cpu_state.x) as u16);
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::IndirectIndexed => {
                dr.addr_init         = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.addr_intermediate = Some(mem.read16(dr.addr_init.unwrap()).unwrap());
                dr.addr_final        = Some( ((dr.addr_intermediate.unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Relative        => {
                dr.value_final = Some(mem.read8(cpu_state.pc+1).unwrap());

                // NOTE: relative is from the end of the current instruction and relative
                // instructions are 2 bytes long, so we add 2 before adding in the specified offset
                //
                if dr.value_final.unwrap() < 0x80 { dr.addr_final  = Some(cpu_state.pc + 2 + dr.value_final.unwrap() as u16); }
                else                              { dr.addr_final  = Some(cpu_state.pc + 2 + dr.value_final.unwrap() as u16 - 0x100);}
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageX       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.x as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageY       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.y as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            _ => { return Err(ExecutionError::UnexpectedAddressMode(format!("unrecognized addressing mode '{:?}' while decoding instruction_register!",dr.info.address_mode))); }
        }

        return Ok(dr);
    }

    /// Perform the current instruction, returning the CpuState after execution.
    pub fn execute(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) -> Result<(),ExecutionError> {
    	// Figure out which opcode is being executed.
    	match cpu_state.decode_register.info.opcode_class {
    		OpcodeClass::ASL => {
				cpu_state.a = cpu_state.a << 1;
				cpu_state.C = (128 & cpu_state.a) > 0;
    			if cpu_state.a == 0 { cpu_state.Z = true; }

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::AND => {
				cpu_state.a = cpu_state.a & cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.a);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::BCC => {
                if !cpu_state.C { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else            { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BCS => {
                if cpu_state.C { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else           { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BEQ => {
                if cpu_state.Z { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else           { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BIT => {
                cpu_state.V = (cpu_state.decode_register.value_final.unwrap() >> 6) & 1 > 0;
                set_z!(cpu_state,cpu_state.decode_register.value_final.unwrap() & cpu_state.a);
                set_s!(cpu_state,cpu_state.decode_register.value_final.unwrap());

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::BNE => {
                if !cpu_state.Z { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else            { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BPL => {
                if !cpu_state.S { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else            { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BVC => {
                if !cpu_state.V { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else            { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BVS => {
                if cpu_state.V { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else           { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::CMP => {
                compare!(cpu_state,cpu_state.a,cpu_state.decode_register.value_final.unwrap());

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::CLC => {
                cpu_state.C = false;
    		},
    		OpcodeClass::CLD => {
                cpu_state.D = false;
    		},
    		OpcodeClass::LDA => {
    			cpu_state.a = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.a);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LDX => {
    			cpu_state.x = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.x);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LDY => {
    			cpu_state.y = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.y);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LSR => {
    			cpu_state.a = cpu_state.a >> 1;
    			cpu_state.C = 1 & cpu_state.a > 0;
    			if cpu_state.a == 0 { cpu_state.Z = true; }

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::JMP => {
    			cpu_state.pc = cpu_state.decode_register.addr_final.unwrap();
    		},
    		OpcodeClass::JSR => {
                stack_push16!(cpu_state,mem,cpu_state.pc+1);
    			cpu_state.pc = cpu_state.decode_register.addr_final.unwrap();
    		},
    		OpcodeClass::NOP => {
            },
    		OpcodeClass::PHA => {
                stack_push8!(cpu_state,mem,cpu_state.a);
            },
            // http://wiki.nesdev.com/w/index.php/Status_flags
    		OpcodeClass::PHP => {
                stack_push8!(cpu_state,mem,cpu_state.unpack_flags() | 0x30);
            },
    		OpcodeClass::PLA => {
                cpu_state.a = stack_pull8!(cpu_state,mem).unwrap();
                set_zs!(cpu_state,cpu_state.a);
            },
            //http://wiki.nesdev.com/w/index.php/Status_flags
    		OpcodeClass::PLP => {
                let val = (stack_pull8!(cpu_state,mem).unwrap()&0xEF) | 0x20;
                cpu_state.pack_flags(val);
            },
    		OpcodeClass::RTS => {
                cpu_state.pc = stack_pull16!(cpu_state,mem).unwrap() + 1;
            },
    		OpcodeClass::SEC => {
                cpu_state.C = true;
    		},
    		OpcodeClass::SED => {
                cpu_state.D = true;
    		},
    		OpcodeClass::SEI => {
                cpu_state.I = true;
    		},
    		OpcodeClass::STA => {
                try!(mem.write8(cpu_state.decode_register.addr_final.unwrap(),cpu_state.a));
                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::STX => {
                try!(mem.write8(cpu_state.decode_register.addr_final.unwrap(),cpu_state.x));
                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},

			_ => { return Err(ExecutionError::UnexpectedOpcode(format!("Unrecognised opcode class: {:?}", cpu_state.decode_register.info.opcode_class)));}

    	}
        Ok(())
    }
}

<|endoftext|>