<commit_before>use crate::VALIDATOR_DIR_FLAG;
use clap::{App, Arg, ArgMatches};
use deposit_contract::DEPOSIT_GAS;
use environment::Environment;
use futures::compat::Future01CompatExt;
use slog::{info, Logger};
use std::path::PathBuf;
use tokio::time::{delay_until, Duration, Instant};
use types::EthSpec;
use validator_dir::{Eth1DepositData, Manager as ValidatorManager, ValidatorDir};
use web3::{
    transports::Http,
    transports::Ipc,
    types::{Address, SyncInfo, SyncState, TransactionRequest, U256},
    Transport, Web3,
};

pub const CMD: &str = "deposit";
pub const VALIDATOR_FLAG: &str = "validator";
pub const ETH1_IPC_FLAG: &str = "eth1-ipc";
pub const ETH1_HTTP_FLAG: &str = "eth1-http";
pub const FROM_ADDRESS_FLAG: &str = "from-address";

const GWEI: u64 = 1_000_000_000;

const SYNCING_STATE_RETRY_DELAY: Duration = Duration::from_secs(2);

pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
    App::new("deposit")
        .about(
            "Submits a deposit to an Eth1 validator registration contract via an IPC endpoint \
            of an Eth1 client (e.g., Geth, OpenEthereum, etc.). The validators must already \
            have been created and exist on the file-system. The process will exit immediately \
            with an error if any error occurs. After each deposit is submitted to the Eth1 \
            node, a file will be saved in the validator directory with the transaction hash. \
            The application does not wait for confirmations so there is not guarantee that \
            the transaction is included in the Eth1 chain; use a block explorer and the \
            transaction hash to check for confirmations. The deposit contract address will \
            be determined by the --testnet-dir flag on the primary Lighthouse binary.",
        )
        .arg(
            Arg::with_name(VALIDATOR_DIR_FLAG)
                .long(VALIDATOR_DIR_FLAG)
                .value_name("VALIDATOR_DIRECTORY")
                .help(
                    "The path the validator client data directory. \
                    Defaults to ~/.lighthouse/validators",
                )
                .takes_value(true),
        )
        .arg(
            Arg::with_name(VALIDATOR_FLAG)
                .long(VALIDATOR_FLAG)
                .value_name("VALIDATOR_NAME")
                .help(
                    "The name of the directory in --data-dir for which to deposit. \
                    Set to 'all' to deposit all validators in the --data-dir.",
                )
                .takes_value(true)
                .required(true),
        )
        .arg(
            Arg::with_name(ETH1_IPC_FLAG)
                .long(ETH1_IPC_FLAG)
                .value_name("ETH1_IPC_PATH")
                .help("Path to an Eth1 JSON-RPC IPC endpoint")
                .takes_value(true)
                .required(false),
        )
        .arg(
            Arg::with_name(ETH1_HTTP_FLAG)
                .long(ETH1_HTTP_FLAG)
                .value_name("ETH1_HTTP_URL")
                .help("URL to an Eth1 JSON-RPC endpoint")
                .takes_value(true)
                .required(false),
        )
        .arg(
            Arg::with_name(FROM_ADDRESS_FLAG)
                .long(FROM_ADDRESS_FLAG)
                .value_name("FROM_ETH1_ADDRESS")
                .help(
                    "The address that will submit the eth1 deposit. \
                    Must be unlocked on the node at --eth1-ipc.",
                )
                .takes_value(true)
                .required(true),
        )
}

fn send_deposit_transactions<T1, T2: 'static>(
    mut env: Environment<T1>,
    log: Logger,
    eth1_deposit_datas: Vec<(ValidatorDir, Eth1DepositData)>,
    from_address: Address,
    deposit_contract: Address,
    transport: T2,
) -> Result<(), String>
where
    T1: EthSpec,
    T2: Transport + std::marker::Send,
    <T2 as web3::Transport>::Out: std::marker::Send,
{
    let web3 = Web3::new(transport);

    let deposits_fut = async {
        poll_until_synced(web3.clone(), log.clone()).await?;

        for (mut validator_dir, eth1_deposit_data) in eth1_deposit_datas {
            let tx_hash = web3
                .eth()
                .send_transaction(TransactionRequest {
                    from: from_address,
                    to: Some(deposit_contract),
                    gas: Some(DEPOSIT_GAS.into()),
                    gas_price: None,
                    value: Some(from_gwei(eth1_deposit_data.deposit_data.amount)),
                    data: Some(eth1_deposit_data.rlp.into()),
                    nonce: None,
                    condition: None,
                })
                .compat()
                .await
                .map_err(|e| format!("Failed to send transaction: {:?}", e))?;

            info!(
                log,
                "Submitted deposit";
                "tx_hash" => format!("{:?}", tx_hash),
            );

            validator_dir
                .save_eth1_deposit_tx_hash(&format!("{:?}", tx_hash))
                .map_err(|e| format!("Failed to save tx hash {:?} to disk: {:?}", tx_hash, e))?;
        }

        Ok::<(), String>(())
    };

    env.runtime().block_on(deposits_fut)?;

    Ok(())
}

pub fn cli_run<T: EthSpec>(
    matches: &ArgMatches<'_>,
    mut env: Environment<T>,
) -> Result<(), String> {
    let log = env.core_context().log().clone();

    let data_dir = clap_utils::parse_path_with_default_in_home_dir(
        matches,
        VALIDATOR_DIR_FLAG,
        PathBuf::new().join(".lighthouse").join("validators"),
    )?;
    let validator: String = clap_utils::parse_required(matches, VALIDATOR_FLAG)?;
    let eth1_ipc_path: Option<PathBuf> = clap_utils::parse_optional(matches, ETH1_IPC_FLAG)?;
    let eth1_http_url: Option<String> = clap_utils::parse_optional(matches, ETH1_HTTP_FLAG)?;
    let from_address: Address = clap_utils::parse_required(matches, FROM_ADDRESS_FLAG)?;

    let manager = ValidatorManager::open(&data_dir)
        .map_err(|e| format!("Unable to read --{}: {:?}", VALIDATOR_DIR_FLAG, e))?;

    let validators = match validator.as_ref() {
        "all" => manager
            .open_all_validators()
            .map_err(|e| format!("Unable to read all validators: {:?}", e)),
        name => {
            let path = manager
                .directory_names()
                .map_err(|e| {
                    format!(
                        "Unable to read --{} directory names: {:?}",
                        VALIDATOR_DIR_FLAG, e
                    )
                })?
                .get(name)
                .ok_or_else(|| format!("Unknown validator:  {}", name))?
                .clone();

            manager
                .open_validator(&path)
                .map_err(|e| format!("Unable to open {}: {:?}", name, e))
                .map(|v| vec![v])
        }
    }?;

    let eth1_deposit_datas = validators
        .into_iter()
        .filter(|v| !v.eth1_deposit_tx_hash_exists())
        .map(|v| match v.eth1_deposit_data() {
            Ok(Some(data)) => Ok((v, data)),
            Ok(None) => Err(format!(
                "Validator is missing deposit data file: {:?}",
                v.dir()
            )),
            Err(e) => Err(format!(
                "Unable to read deposit data for {:?}: {:?}",
                v.dir(),
                e
            )),
        })
        .collect::<Result<Vec<_>, _>>()?;

    let total_gwei: u64 = eth1_deposit_datas
        .iter()
        .map(|(_, d)| d.deposit_data.amount)
        .sum();

    if eth1_deposit_datas.is_empty() {
        info!(log, "No validators to deposit");

        return Ok(());
    }

    info!(
        log,
        "Starting deposits";
        "deposit_count" => eth1_deposit_datas.len(),
        "total_eth" => total_gwei / GWEI,
    );

    let deposit_contract = env
        .testnet
        .as_ref()
        .ok_or_else(|| "Unable to run account manager without a testnet dir".to_string())?
        .deposit_contract_address()
        .map_err(|e| format!("Unable to parse deposit contract address: {}", e))?;

    if deposit_contract == Address::zero() {
        return Err("Refusing to deposit to the zero address. Check testnet configuration.".into());
    }

    match (eth1_ipc_path, eth1_http_url) {
        (Some(_), Some(_)) => Err(format!(
            "error: Cannot supply both --{} and --{}",
            ETH1_IPC_FLAG, ETH1_HTTP_FLAG
        )),
        (None, None) => Err(format!(
            "error: Must supply one of --{} or --{}",
            ETH1_IPC_FLAG, ETH1_HTTP_FLAG
        )),
        (Some(ipc_path), None) => {
            let (_event_loop_handle, ipc_transport) = Ipc::new(ipc_path)
                .map_err(|e| format!("Unable to connect to eth1 IPC: {:?}", e))?;
            send_deposit_transactions(
                env,
                log,
                eth1_deposit_datas,
                from_address,
                deposit_contract,
                ipc_transport,
            )
        }
        (None, Some(http_url)) => {
            let (_event_loop_handle, http_transport) = Http::new(http_url.as_str())
                .map_err(|e| format!("Unable to connect to eth1 http RPC: {:?}", e))?;
            send_deposit_transactions(
                env,
                log,
                eth1_deposit_datas,
                from_address,
                deposit_contract,
                http_transport,
            )
        }
    }
}

/// Converts gwei to wei.
fn from_gwei(gwei: u64) -> U256 {
    U256::from(gwei) * U256::exp10(9)
}

/// Run a poll on the `eth_syncing` endpoint, blocking until the node is synced.
async fn poll_until_synced<T>(web3: Web3<T>, log: Logger) -> Result<(), String>
where
    T: Transport + Send + 'static,
    <T as Transport>::Out: Send,
{
    loop {
        let sync_state = web3
            .clone()
            .eth()
            .syncing()
            .compat()
            .await
            .map_err(|e| format!("Unable to read syncing state from eth1 node: {:?}", e))?;

        match sync_state {
            SyncState::Syncing(SyncInfo {
                current_block,
                highest_block,
                ..
            }) => {
                info!(
                    log,
                    "Waiting for eth1 node to sync";
                    "est_highest_block" => format!("{}", highest_block),
                    "current_block" => format!("{}", current_block),
                );

                delay_until(Instant::now() + SYNCING_STATE_RETRY_DELAY).await;
            }
            SyncState::NotSyncing => {
                let block_number = web3
                    .clone()
                    .eth()
                    .block_number()
                    .compat()
                    .await
                    .map_err(|e| format!("Unable to read block number from eth1 node: {:?}", e))?;

                if block_number > 0.into() {
                    info!(
                        log,
                        "Eth1 node is synced";
                        "head_block" => format!("{}", block_number),
                    );
                    break;
                } else {
                    delay_until(Instant::now() + SYNCING_STATE_RETRY_DELAY).await;
                    info!(
                        log,
                        "Waiting for eth1 node to sync";
                        "current_block" => 0,
                    );
                }
            }
        }
    }

    Ok(())
}
<commit_msg>Add eth1 deposit confirmations (#1370)<commit_after>use crate::VALIDATOR_DIR_FLAG;
use clap::{App, Arg, ArgMatches};
use deposit_contract::DEPOSIT_GAS;
use environment::Environment;
use futures::{
    compat::Future01CompatExt,
    stream::{FuturesUnordered, StreamExt},
};
use slog::{info, Logger};
use std::path::PathBuf;
use tokio::time::{delay_until, Duration, Instant};
use types::EthSpec;
use validator_dir::{Eth1DepositData, Manager as ValidatorManager, ValidatorDir};
use web3::{
    transports::Http,
    transports::Ipc,
    types::{Address, SyncInfo, SyncState, TransactionRequest, U256},
    Transport, Web3,
};

pub const CMD: &str = "deposit";
pub const VALIDATOR_FLAG: &str = "validator";
pub const ETH1_IPC_FLAG: &str = "eth1-ipc";
pub const ETH1_HTTP_FLAG: &str = "eth1-http";
pub const FROM_ADDRESS_FLAG: &str = "from-address";
pub const CONFIRMATION_COUNT_FLAG: &str = "confirmation-count";
pub const CONFIRMATION_BATCH_SIZE_FLAG: &str = "confirmation-batch-size";

const GWEI: u64 = 1_000_000_000;

const SYNCING_STATE_RETRY_DELAY: Duration = Duration::from_secs(2);

const CONFIRMATIONS_POLL_TIME: Duration = Duration::from_secs(2);

pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
    App::new("deposit")
        .about(
            "Submits a deposit to an Eth1 validator registration contract via an IPC endpoint \
            of an Eth1 client (e.g., Geth, OpenEthereum, etc.). The validators must already \
            have been created and exist on the file-system. The process will exit immediately \
            with an error if any error occurs. After each deposit is submitted to the Eth1 \
            node, a file will be saved in the validator directory with the transaction hash. \
            If confirmations are set to non-zero then the application will wait for confirmations \
            before saving the transaction hash and moving onto the next batch of deposits. \
            The deposit contract address will be determined by the --testnet-dir flag on the \
            primary Lighthouse binary.",
        )
        .arg(
            Arg::with_name(VALIDATOR_DIR_FLAG)
                .long(VALIDATOR_DIR_FLAG)
                .value_name("VALIDATOR_DIRECTORY")
                .help(
                    "The path the validator client data directory. \
                    Defaults to ~/.lighthouse/validators",
                )
                .takes_value(true),
        )
        .arg(
            Arg::with_name(VALIDATOR_FLAG)
                .long(VALIDATOR_FLAG)
                .value_name("VALIDATOR_NAME")
                .help(
                    "The name of the directory in --data-dir for which to deposit. \
                    Set to 'all' to deposit all validators in the --data-dir.",
                )
                .takes_value(true)
                .required(true),
        )
        .arg(
            Arg::with_name(ETH1_IPC_FLAG)
                .long(ETH1_IPC_FLAG)
                .value_name("ETH1_IPC_PATH")
                .help("Path to an Eth1 JSON-RPC IPC endpoint")
                .takes_value(true)
                .required(false),
        )
        .arg(
            Arg::with_name(ETH1_HTTP_FLAG)
                .long(ETH1_HTTP_FLAG)
                .value_name("ETH1_HTTP_URL")
                .help("URL to an Eth1 JSON-RPC endpoint")
                .takes_value(true)
                .required(false),
        )
        .arg(
            Arg::with_name(FROM_ADDRESS_FLAG)
                .long(FROM_ADDRESS_FLAG)
                .value_name("FROM_ETH1_ADDRESS")
                .help(
                    "The address that will submit the eth1 deposit. \
                    Must be unlocked on the node at --eth1-ipc.",
                )
                .takes_value(true)
                .required(true),
        )
        .arg(
            Arg::with_name(CONFIRMATION_COUNT_FLAG)
                .long(CONFIRMATION_COUNT_FLAG)
                .value_name("CONFIRMATION_COUNT")
                .help(
                    "The number of Eth1 block confirmations required \
                    before a transaction is considered complete. Set to \
                    0 for no confirmations.",
                )
                .takes_value(true)
                .default_value("1"),
        )
        .arg(
            Arg::with_name(CONFIRMATION_BATCH_SIZE_FLAG)
                .long(CONFIRMATION_BATCH_SIZE_FLAG)
                .value_name("BATCH_SIZE")
                .help(
                    "Perform BATCH_SIZE deposits and wait for confirmations \
                    in parallel. Useful for achieving faster bulk deposits.",
                )
                .takes_value(true)
                .default_value("10"),
        )
}

#[allow(clippy::too_many_arguments)]
fn send_deposit_transactions<T1, T2: 'static>(
    mut env: Environment<T1>,
    log: Logger,
    mut eth1_deposit_datas: Vec<(ValidatorDir, Eth1DepositData)>,
    from_address: Address,
    deposit_contract: Address,
    transport: T2,
    confirmation_count: usize,
    confirmation_batch_size: usize,
) -> Result<(), String>
where
    T1: EthSpec,
    T2: Transport + std::marker::Send,
    <T2 as web3::Transport>::Out: std::marker::Send,
{
    let web3 = Web3::new(transport);

    let deposits_fut = async {
        poll_until_synced(web3.clone(), log.clone()).await?;

        for chunk in eth1_deposit_datas.chunks_mut(confirmation_batch_size) {
            let futures = FuturesUnordered::default();

            for (ref mut validator_dir, eth1_deposit_data) in chunk.iter_mut() {
                let web3 = web3.clone();
                let log = log.clone();
                futures.push(async move {
                    let tx_hash = web3
                        .send_transaction_with_confirmation(
                            TransactionRequest {
                                from: from_address,
                                to: Some(deposit_contract),
                                gas: Some(DEPOSIT_GAS.into()),
                                gas_price: None,
                                value: Some(from_gwei(eth1_deposit_data.deposit_data.amount)),
                                data: Some(eth1_deposit_data.rlp.clone().into()),
                                nonce: None,
                                condition: None,
                            },
                            CONFIRMATIONS_POLL_TIME,
                            confirmation_count,
                        )
                        .compat()
                        .await
                        .map_err(|e| format!("Failed to send transaction: {:?}", e))?;

                    info!(
                        log,
                        "Submitted deposit";
                        "tx_hash" => format!("{:?}", tx_hash),
                    );

                    validator_dir
                        .save_eth1_deposit_tx_hash(&format!("{:?}", tx_hash))
                        .map_err(|e| {
                            format!("Failed to save tx hash {:?} to disk: {:?}", tx_hash, e)
                        })?;

                    Ok::<(), String>(())
                });
            }

            futures
                .collect::<Vec<_>>()
                .await
                .into_iter()
                .collect::<Result<_, _>>()?;
        }

        Ok::<(), String>(())
    };

    env.runtime().block_on(deposits_fut)?;

    Ok(())
}

pub fn cli_run<T: EthSpec>(
    matches: &ArgMatches<'_>,
    mut env: Environment<T>,
) -> Result<(), String> {
    let log = env.core_context().log().clone();

    let data_dir = clap_utils::parse_path_with_default_in_home_dir(
        matches,
        VALIDATOR_DIR_FLAG,
        PathBuf::new().join(".lighthouse").join("validators"),
    )?;
    let validator: String = clap_utils::parse_required(matches, VALIDATOR_FLAG)?;
    let eth1_ipc_path: Option<PathBuf> = clap_utils::parse_optional(matches, ETH1_IPC_FLAG)?;
    let eth1_http_url: Option<String> = clap_utils::parse_optional(matches, ETH1_HTTP_FLAG)?;
    let from_address: Address = clap_utils::parse_required(matches, FROM_ADDRESS_FLAG)?;
    let confirmation_count: usize = clap_utils::parse_required(matches, CONFIRMATION_COUNT_FLAG)?;
    let confirmation_batch_size: usize =
        clap_utils::parse_required(matches, CONFIRMATION_BATCH_SIZE_FLAG)?;

    let manager = ValidatorManager::open(&data_dir)
        .map_err(|e| format!("Unable to read --{}: {:?}", VALIDATOR_DIR_FLAG, e))?;

    let validators = match validator.as_ref() {
        "all" => manager
            .open_all_validators()
            .map_err(|e| format!("Unable to read all validators: {:?}", e)),
        name => {
            let path = manager
                .directory_names()
                .map_err(|e| {
                    format!(
                        "Unable to read --{} directory names: {:?}",
                        VALIDATOR_DIR_FLAG, e
                    )
                })?
                .get(name)
                .ok_or_else(|| format!("Unknown validator:  {}", name))?
                .clone();

            manager
                .open_validator(&path)
                .map_err(|e| format!("Unable to open {}: {:?}", name, e))
                .map(|v| vec![v])
        }
    }?;

    let eth1_deposit_datas = validators
        .into_iter()
        .filter(|v| !v.eth1_deposit_tx_hash_exists())
        .map(|v| match v.eth1_deposit_data() {
            Ok(Some(data)) => Ok((v, data)),
            Ok(None) => Err(format!(
                "Validator is missing deposit data file: {:?}",
                v.dir()
            )),
            Err(e) => Err(format!(
                "Unable to read deposit data for {:?}: {:?}",
                v.dir(),
                e
            )),
        })
        .collect::<Result<Vec<_>, _>>()?;

    let total_gwei: u64 = eth1_deposit_datas
        .iter()
        .map(|(_, d)| d.deposit_data.amount)
        .sum();

    if eth1_deposit_datas.is_empty() {
        info!(log, "No validators to deposit");

        return Ok(());
    }

    info!(
        log,
        "Starting deposits";
        "deposit_count" => eth1_deposit_datas.len(),
        "total_eth" => total_gwei / GWEI,
    );

    let deposit_contract = env
        .testnet
        .as_ref()
        .ok_or_else(|| "Unable to run account manager without a testnet dir".to_string())?
        .deposit_contract_address()
        .map_err(|e| format!("Unable to parse deposit contract address: {}", e))?;

    if deposit_contract == Address::zero() {
        return Err("Refusing to deposit to the zero address. Check testnet configuration.".into());
    }

    match (eth1_ipc_path, eth1_http_url) {
        (Some(_), Some(_)) => Err(format!(
            "error: Cannot supply both --{} and --{}",
            ETH1_IPC_FLAG, ETH1_HTTP_FLAG
        )),
        (None, None) => Err(format!(
            "error: Must supply one of --{} or --{}",
            ETH1_IPC_FLAG, ETH1_HTTP_FLAG
        )),
        (Some(ipc_path), None) => {
            let (_event_loop_handle, ipc_transport) = Ipc::new(ipc_path)
                .map_err(|e| format!("Unable to connect to eth1 IPC: {:?}", e))?;
            send_deposit_transactions(
                env,
                log,
                eth1_deposit_datas,
                from_address,
                deposit_contract,
                ipc_transport,
                confirmation_count,
                confirmation_batch_size,
            )
        }
        (None, Some(http_url)) => {
            let (_event_loop_handle, http_transport) = Http::new(http_url.as_str())
                .map_err(|e| format!("Unable to connect to eth1 http RPC: {:?}", e))?;
            send_deposit_transactions(
                env,
                log,
                eth1_deposit_datas,
                from_address,
                deposit_contract,
                http_transport,
                confirmation_count,
                confirmation_batch_size,
            )
        }
    }
}

/// Converts gwei to wei.
fn from_gwei(gwei: u64) -> U256 {
    U256::from(gwei) * U256::exp10(9)
}

/// Run a poll on the `eth_syncing` endpoint, blocking until the node is synced.
async fn poll_until_synced<T>(web3: Web3<T>, log: Logger) -> Result<(), String>
where
    T: Transport + Send + 'static,
    <T as Transport>::Out: Send,
{
    loop {
        let sync_state = web3
            .clone()
            .eth()
            .syncing()
            .compat()
            .await
            .map_err(|e| format!("Unable to read syncing state from eth1 node: {:?}", e))?;

        match sync_state {
            SyncState::Syncing(SyncInfo {
                current_block,
                highest_block,
                ..
            }) => {
                info!(
                    log,
                    "Waiting for eth1 node to sync";
                    "est_highest_block" => format!("{}", highest_block),
                    "current_block" => format!("{}", current_block),
                );

                delay_until(Instant::now() + SYNCING_STATE_RETRY_DELAY).await;
            }
            SyncState::NotSyncing => {
                let block_number = web3
                    .clone()
                    .eth()
                    .block_number()
                    .compat()
                    .await
                    .map_err(|e| format!("Unable to read block number from eth1 node: {:?}", e))?;

                if block_number > 0.into() {
                    info!(
                        log,
                        "Eth1 node is synced";
                        "head_block" => format!("{}", block_number),
                    );
                    break;
                } else {
                    delay_until(Instant::now() + SYNCING_STATE_RETRY_DELAY).await;
                    info!(
                        log,
                        "Waiting for eth1 node to sync";
                        "current_block" => 0,
                    );
                }
            }
        }
    }

    Ok(())
}
<|endoftext|>


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

#![forbid(unsafe_code)]

use crate::cli::Options;
use abigen::Abigen;
use anyhow::anyhow;
use boogie_backend::{
    add_prelude, boogie_wrapper::BoogieWrapper, bytecode_translator::BoogieTranslator,
};
use bytecode::{
    function_target_pipeline::{FunctionTargetPipeline, FunctionTargetsHolder},
    pipeline_factory,
    read_write_set_analysis::{self, ReadWriteSetProcessor},
};
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor};
use docgen::Docgen;
use errmapgen::ErrmapGen;
#[allow(unused_imports)]
use log::{debug, info, warn};
use move_model::{
    code_writer::CodeWriter, model::GlobalEnv, parse_addresses_from_options,
    run_model_builder_with_options,
};
use std::{fs, path::PathBuf, time::Instant};

pub mod cli;

// =================================================================================================
// Prover API

pub fn run_move_prover_errors_to_stderr(options: Options) -> anyhow::Result<()> {
    let mut error_writer = StandardStream::stderr(ColorChoice::Auto);
    run_move_prover(&mut error_writer, options)
}

pub fn run_move_prover<W: WriteColor>(
    error_writer: &mut W,
    options: Options,
) -> anyhow::Result<()> {
    let now = Instant::now();

    // Run the model builder.
    let env = run_model_builder_with_options(
        &options.move_sources,
        &options.move_deps,
        options.model_builder.clone(),
        parse_addresses_from_options(options.move_named_address_values.clone())?,
    )?;
    let build_duration = now.elapsed();
    check_errors(
        &env,
        &options,
        error_writer,
        "exiting with model building errors",
    )?;
    env.report_diag(error_writer, options.prover.report_severity);

    // Add the prover options as an extension to the environment, so they can be accessed
    // from there.
    env.set_extension(options.prover.clone());

    // Until this point, prover and docgen have same code. Here we part ways.
    if options.run_docgen {
        return run_docgen(&env, &options, error_writer, now);
    }
    // Same for ABI generator.
    if options.run_abigen {
        return run_abigen(&env, &options, now);
    }
    // Same for the error map generator
    if options.run_errmapgen {
        return {
            run_errmapgen(&env, &options, now);
            Ok(())
        };
    }
    // Same for read/write set analysis
    if options.run_read_write_set {
        return {
            run_read_write_set(&env, &options, now);
            Ok(())
        };
    }

    // Check correct backend versions.
    options.backend.check_tool_versions()?;

    // Create and process bytecode
    let now = Instant::now();
    let targets = create_and_process_bytecode(&options, &env);
    let trafo_duration = now.elapsed();
    check_errors(
        &env,
        &options,
        error_writer,
        "exiting with bytecode transformation errors",
    )?;

    // Generate boogie code
    let now = Instant::now();
    let code_writer = generate_boogie(&env, &options, &targets)?;
    let gen_duration = now.elapsed();
    check_errors(
        &env,
        &options,
        error_writer,
        "exiting with boogie generation errors",
    )?;

    // Verify boogie code.
    let now = Instant::now();
    verify_boogie(&env, &options, &targets, code_writer)?;
    let verify_duration = now.elapsed();

    // Report durations.
    info!(
        "{:.3}s build, {:.3}s trafo, {:.3}s gen, {:.3}s verify",
        build_duration.as_secs_f64(),
        trafo_duration.as_secs_f64(),
        gen_duration.as_secs_f64(),
        verify_duration.as_secs_f64()
    );
    check_errors(
        &env,
        &options,
        error_writer,
        "exiting with boogie verification errors",
    )
}

pub fn check_errors<W: WriteColor>(
    env: &GlobalEnv,
    options: &Options,
    error_writer: &mut W,
    msg: &'static str,
) -> anyhow::Result<()> {
    env.report_diag(error_writer, options.prover.report_severity);
    if env.has_errors() {
        Err(anyhow!(msg))
    } else {
        Ok(())
    }
}

pub fn generate_boogie(
    env: &GlobalEnv,
    options: &Options,
    targets: &FunctionTargetsHolder,
) -> anyhow::Result<CodeWriter> {
    let writer = CodeWriter::new(env.internal_loc());
    add_prelude(env, &options.backend, &writer)?;
    let mut translator = BoogieTranslator::new(env, &options.backend, targets, &writer);
    translator.translate();
    Ok(writer)
}

pub fn verify_boogie(
    env: &GlobalEnv,
    options: &Options,
    targets: &FunctionTargetsHolder,
    writer: CodeWriter,
) -> anyhow::Result<()> {
    let output_existed = std::path::Path::new(&options.output_path).exists();
    debug!("writing boogie to `{}`", &options.output_path);
    writer.process_result(|result| fs::write(&options.output_path, result))?;
    let boogie = BoogieWrapper {
        env,
        targets,
        writer: &writer,
        options: &options.backend,
    };
    boogie.call_boogie_and_verify_output(&options.output_path)?;
    if !output_existed && !options.backend.keep_artifacts {
        std::fs::remove_file(&options.output_path).unwrap_or_default();
    }
    Ok(())
}

/// Create bytecode and process it.
pub fn create_and_process_bytecode(options: &Options, env: &GlobalEnv) -> FunctionTargetsHolder {
    let mut targets = FunctionTargetsHolder::default();

    // Add function targets for all functions in the environment.
    for module_env in env.get_modules() {
        if options.prover.dump_bytecode {
            let output_file = options
                .move_sources
                .get(0)
                .cloned()
                .unwrap_or_else(|| "bytecode".to_string())
                .replace(".move", ".mv.disas");
            fs::write(&output_file, &module_env.disassemble())
                .expect("dumping disassembled module");
        }
        for func_env in module_env.get_functions() {
            targets.add_target(&func_env)
        }
    }

    // Create processing pipeline and run it.
    let pipeline = if options.experimental_pipeline {
        pipeline_factory::experimental_pipeline()
    } else {
        pipeline_factory::default_pipeline_with_options(&options.prover)
    };

    if options.prover.dump_bytecode {
        let dump_file = options
            .move_sources
            .get(0)
            .cloned()
            .unwrap_or_else(|| "bytecode".to_string())
            .replace(".move", "");
        pipeline.run_with_dump(env, &mut targets, &dump_file, options.prover.dump_cfg)
    } else {
        pipeline.run(env, &mut targets);
    }

    targets
}

// Tools using the Move prover top-level driver
// ============================================

// TODO: make those tools independent. Need to first address the todo to
// move the model builder into the move-model crate.

fn run_docgen<W: WriteColor>(
    env: &GlobalEnv,
    options: &Options,
    error_writer: &mut W,
    now: Instant,
) -> anyhow::Result<()> {
    let generator = Docgen::new(env, &options.docgen);
    let checking_elapsed = now.elapsed();
    info!("generating documentation");
    for (file, content) in generator.gen() {
        let path = PathBuf::from(&file);
        fs::create_dir_all(path.parent().unwrap())?;
        fs::write(path.as_path(), content)?;
    }
    let generating_elapsed = now.elapsed();
    info!(
        "{:.3}s checking, {:.3}s generating",
        checking_elapsed.as_secs_f64(),
        (generating_elapsed - checking_elapsed).as_secs_f64()
    );
    if env.has_errors() {
        env.report_diag(error_writer, options.prover.report_severity);
        Err(anyhow!("exiting with documentation generation errors"))
    } else {
        Ok(())
    }
}

fn run_abigen(env: &GlobalEnv, options: &Options, now: Instant) -> anyhow::Result<()> {
    let mut generator = Abigen::new(env, &options.abigen);
    let checking_elapsed = now.elapsed();
    info!("generating ABI files");
    generator.gen();
    for (file, content) in generator.into_result() {
        let path = PathBuf::from(&file);
        fs::create_dir_all(path.parent().unwrap())?;
        fs::write(path.as_path(), content)?;
    }
    let generating_elapsed = now.elapsed();
    info!(
        "{:.3}s checking, {:.3}s generating",
        checking_elapsed.as_secs_f64(),
        (generating_elapsed - checking_elapsed).as_secs_f64()
    );
    Ok(())
}

fn run_errmapgen(env: &GlobalEnv, options: &Options, now: Instant) {
    let mut generator = ErrmapGen::new(env, &options.errmapgen);
    let checking_elapsed = now.elapsed();
    info!("generating error map");
    generator.gen();
    generator.save_result();
    let generating_elapsed = now.elapsed();
    info!(
        "{:.3}s checking, {:.3}s generating",
        checking_elapsed.as_secs_f64(),
        (generating_elapsed - checking_elapsed).as_secs_f64()
    );
}

fn run_read_write_set(env: &GlobalEnv, options: &Options, now: Instant) {
    let mut targets = FunctionTargetsHolder::default();

    for module_env in env.get_modules() {
        for func_env in module_env.get_functions() {
            targets.add_target(&func_env)
        }
    }
    let mut pipeline = FunctionTargetPipeline::default();
    pipeline.add_processor(ReadWriteSetProcessor::new());

    let start = now.elapsed();
    info!("generating read/write set");
    pipeline.run(env, &mut targets);
    read_write_set_analysis::get_read_write_set(env, &targets);
    println!("generated for {:?}", options.move_sources);

    let end = now.elapsed();
    info!("{:.3}s analyzing", (end - start).as_secs_f64());
}
<commit_msg>[move-prover] dump bytecode and result in output dir<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use crate::cli::Options;
use abigen::Abigen;
use anyhow::anyhow;
use boogie_backend::{
    add_prelude, boogie_wrapper::BoogieWrapper, bytecode_translator::BoogieTranslator,
};
use bytecode::{
    function_target_pipeline::{FunctionTargetPipeline, FunctionTargetsHolder},
    pipeline_factory,
    read_write_set_analysis::{self, ReadWriteSetProcessor},
};
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor};
use docgen::Docgen;
use errmapgen::ErrmapGen;
#[allow(unused_imports)]
use log::{debug, info, warn};
use move_model::{
    code_writer::CodeWriter, model::GlobalEnv, parse_addresses_from_options,
    run_model_builder_with_options,
};
use std::{
    fs,
    path::{Path, PathBuf},
    time::Instant,
};

pub mod cli;

// =================================================================================================
// Prover API

pub fn run_move_prover_errors_to_stderr(options: Options) -> anyhow::Result<()> {
    let mut error_writer = StandardStream::stderr(ColorChoice::Auto);
    run_move_prover(&mut error_writer, options)
}

pub fn run_move_prover<W: WriteColor>(
    error_writer: &mut W,
    options: Options,
) -> anyhow::Result<()> {
    let now = Instant::now();

    // Run the model builder.
    let env = run_model_builder_with_options(
        &options.move_sources,
        &options.move_deps,
        options.model_builder.clone(),
        parse_addresses_from_options(options.move_named_address_values.clone())?,
    )?;
    let build_duration = now.elapsed();
    check_errors(
        &env,
        &options,
        error_writer,
        "exiting with model building errors",
    )?;
    env.report_diag(error_writer, options.prover.report_severity);

    // Add the prover options as an extension to the environment, so they can be accessed
    // from there.
    env.set_extension(options.prover.clone());

    // Until this point, prover and docgen have same code. Here we part ways.
    if options.run_docgen {
        return run_docgen(&env, &options, error_writer, now);
    }
    // Same for ABI generator.
    if options.run_abigen {
        return run_abigen(&env, &options, now);
    }
    // Same for the error map generator
    if options.run_errmapgen {
        return {
            run_errmapgen(&env, &options, now);
            Ok(())
        };
    }
    // Same for read/write set analysis
    if options.run_read_write_set {
        return {
            run_read_write_set(&env, &options, now);
            Ok(())
        };
    }

    // Check correct backend versions.
    options.backend.check_tool_versions()?;

    // Create and process bytecode
    let now = Instant::now();
    let targets = create_and_process_bytecode(&options, &env);
    let trafo_duration = now.elapsed();
    check_errors(
        &env,
        &options,
        error_writer,
        "exiting with bytecode transformation errors",
    )?;

    // Generate boogie code
    let now = Instant::now();
    let code_writer = generate_boogie(&env, &options, &targets)?;
    let gen_duration = now.elapsed();
    check_errors(
        &env,
        &options,
        error_writer,
        "exiting with boogie generation errors",
    )?;

    // Verify boogie code.
    let now = Instant::now();
    verify_boogie(&env, &options, &targets, code_writer)?;
    let verify_duration = now.elapsed();

    // Report durations.
    info!(
        "{:.3}s build, {:.3}s trafo, {:.3}s gen, {:.3}s verify",
        build_duration.as_secs_f64(),
        trafo_duration.as_secs_f64(),
        gen_duration.as_secs_f64(),
        verify_duration.as_secs_f64()
    );
    check_errors(
        &env,
        &options,
        error_writer,
        "exiting with boogie verification errors",
    )
}

pub fn check_errors<W: WriteColor>(
    env: &GlobalEnv,
    options: &Options,
    error_writer: &mut W,
    msg: &'static str,
) -> anyhow::Result<()> {
    env.report_diag(error_writer, options.prover.report_severity);
    if env.has_errors() {
        Err(anyhow!(msg))
    } else {
        Ok(())
    }
}

pub fn generate_boogie(
    env: &GlobalEnv,
    options: &Options,
    targets: &FunctionTargetsHolder,
) -> anyhow::Result<CodeWriter> {
    let writer = CodeWriter::new(env.internal_loc());
    add_prelude(env, &options.backend, &writer)?;
    let mut translator = BoogieTranslator::new(env, &options.backend, targets, &writer);
    translator.translate();
    Ok(writer)
}

pub fn verify_boogie(
    env: &GlobalEnv,
    options: &Options,
    targets: &FunctionTargetsHolder,
    writer: CodeWriter,
) -> anyhow::Result<()> {
    let output_existed = std::path::Path::new(&options.output_path).exists();
    debug!("writing boogie to `{}`", &options.output_path);
    writer.process_result(|result| fs::write(&options.output_path, result))?;
    let boogie = BoogieWrapper {
        env,
        targets,
        writer: &writer,
        options: &options.backend,
    };
    boogie.call_boogie_and_verify_output(&options.output_path)?;
    if !output_existed && !options.backend.keep_artifacts {
        std::fs::remove_file(&options.output_path).unwrap_or_default();
    }
    Ok(())
}

/// Create bytecode and process it.
pub fn create_and_process_bytecode(options: &Options, env: &GlobalEnv) -> FunctionTargetsHolder {
    let mut targets = FunctionTargetsHolder::default();
    let output_dir = Path::new(&options.output_path)
        .parent()
        .expect("expect the parent directory of the output path to exist");
    let output_prefix = options.move_sources.get(0).map_or("bytecode", |s| {
        Path::new(s).file_name().unwrap().to_str().unwrap()
    });

    // Add function targets for all functions in the environment.
    for module_env in env.get_modules() {
        if options.prover.dump_bytecode {
            let dump_file = output_dir.join(format!("{}.mv.disas", output_prefix));
            fs::write(&dump_file, &module_env.disassemble()).expect("dumping disassembled module");
        }
        for func_env in module_env.get_functions() {
            targets.add_target(&func_env)
        }
    }

    // Create processing pipeline and run it.
    let pipeline = if options.experimental_pipeline {
        pipeline_factory::experimental_pipeline()
    } else {
        pipeline_factory::default_pipeline_with_options(&options.prover)
    };

    if options.prover.dump_bytecode {
        let dump_file_base = output_dir
            .join(output_prefix)
            .into_os_string()
            .into_string()
            .unwrap();
        pipeline.run_with_dump(env, &mut targets, &dump_file_base, options.prover.dump_cfg)
    } else {
        pipeline.run(env, &mut targets);
    }

    targets
}

// Tools using the Move prover top-level driver
// ============================================

// TODO: make those tools independent. Need to first address the todo to
// move the model builder into the move-model crate.

fn run_docgen<W: WriteColor>(
    env: &GlobalEnv,
    options: &Options,
    error_writer: &mut W,
    now: Instant,
) -> anyhow::Result<()> {
    let generator = Docgen::new(env, &options.docgen);
    let checking_elapsed = now.elapsed();
    info!("generating documentation");
    for (file, content) in generator.gen() {
        let path = PathBuf::from(&file);
        fs::create_dir_all(path.parent().unwrap())?;
        fs::write(path.as_path(), content)?;
    }
    let generating_elapsed = now.elapsed();
    info!(
        "{:.3}s checking, {:.3}s generating",
        checking_elapsed.as_secs_f64(),
        (generating_elapsed - checking_elapsed).as_secs_f64()
    );
    if env.has_errors() {
        env.report_diag(error_writer, options.prover.report_severity);
        Err(anyhow!("exiting with documentation generation errors"))
    } else {
        Ok(())
    }
}

fn run_abigen(env: &GlobalEnv, options: &Options, now: Instant) -> anyhow::Result<()> {
    let mut generator = Abigen::new(env, &options.abigen);
    let checking_elapsed = now.elapsed();
    info!("generating ABI files");
    generator.gen();
    for (file, content) in generator.into_result() {
        let path = PathBuf::from(&file);
        fs::create_dir_all(path.parent().unwrap())?;
        fs::write(path.as_path(), content)?;
    }
    let generating_elapsed = now.elapsed();
    info!(
        "{:.3}s checking, {:.3}s generating",
        checking_elapsed.as_secs_f64(),
        (generating_elapsed - checking_elapsed).as_secs_f64()
    );
    Ok(())
}

fn run_errmapgen(env: &GlobalEnv, options: &Options, now: Instant) {
    let mut generator = ErrmapGen::new(env, &options.errmapgen);
    let checking_elapsed = now.elapsed();
    info!("generating error map");
    generator.gen();
    generator.save_result();
    let generating_elapsed = now.elapsed();
    info!(
        "{:.3}s checking, {:.3}s generating",
        checking_elapsed.as_secs_f64(),
        (generating_elapsed - checking_elapsed).as_secs_f64()
    );
}

fn run_read_write_set(env: &GlobalEnv, options: &Options, now: Instant) {
    let mut targets = FunctionTargetsHolder::default();

    for module_env in env.get_modules() {
        for func_env in module_env.get_functions() {
            targets.add_target(&func_env)
        }
    }
    let mut pipeline = FunctionTargetPipeline::default();
    pipeline.add_processor(ReadWriteSetProcessor::new());

    let start = now.elapsed();
    info!("generating read/write set");
    pipeline.run(env, &mut targets);
    read_write_set_analysis::get_read_write_set(env, &targets);
    println!("generated for {:?}", options.move_sources);

    let end = now.elapsed();
    info!("{:.3}s analyzing", (end - start).as_secs_f64());
}
<|endoftext|>


<commit_before>use vm::{VM, Value, Int, Float, Function, Userdata, StackFrame, Data};
use typecheck::{TcType, Typed, FunctionType, unit_type_tc, bool_type_tc, int_type_tc, float_type_tc};
use compiler::CallGlobal;
use std::any::{Any, AnyRefExt};
use std::boxed::BoxAny;

pub trait VMType {
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType;
    fn make_type(&self, vm: &VM) -> TcType {
        self.vm_type(vm).clone()
    }
}
pub trait VMValue : VMType {
    fn push(self, stack: &mut StackFrame);
    fn from_value(value: Value) -> Option<Self>;
}
impl VMType for () {
    fn vm_type<'a>(&self, _: &'a VM) -> &'a TcType {
        &unit_type_tc
    }
}
impl VMValue for () {
    fn push(self, _: &mut StackFrame) {
    }
    fn from_value(_: Value) -> Option<()> {
        Some(())
    }
}

impl VMType for int {
    fn vm_type<'a>(&self, _: &'a VM) -> &'a TcType {
        &int_type_tc
    }
}
impl VMValue for int {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Int(self));
    }
    fn from_value(value: Value) -> Option<int> {
        match value {
            Int(i) => Some(i),
            _ => None
        }
    }
}
impl VMType for f64 {
    fn vm_type<'a>(&self, _: &'a VM) -> &'a TcType {
        &float_type_tc
    }
}
impl VMValue for f64 {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Float(self));
    }
    fn from_value(value: Value) -> Option<f64> {
        match value {
            Float(f) => Some(f),
            _ => None
        }
    }
}
impl VMType for bool {
    fn vm_type<'a>(&self, _: &'a VM) -> &'a TcType {
        &bool_type_tc
    }
}
impl VMValue for bool {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Int(if self { 1 } else { 0 }));
    }
    fn from_value(value: Value) -> Option<bool> {
        match value {
            Int(1) => Some(true),
            Int(0) => Some(false),
            _ => None
        }
    }
}
impl <T: 'static + BoxAny + Clone> VMType for Box<T> {
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType {
        vm.get_type::<T>()
    }
}
impl <T: 'static + BoxAny + Clone> VMValue for Box<T> {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Userdata(Data::new(self as Box<Any>)));
    }
    fn from_value(value: Value) -> Option<Box<T>> {
        match value {
            Userdata(v) => v.data.borrow().downcast_ref::<T>().map(|x| box x.clone()),
            _ => None
        }
    }
}
impl <T: 'static> VMType for *mut T {
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType {
        vm.get_type::<T>()
    }
}
impl <T: 'static> VMValue for *mut T {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Userdata(Data::new(box self as Box<Any>)));
    }
    fn from_value(value: Value) -> Option<*mut T> {
        match value {
            Userdata(v) => v.data.borrow().downcast_ref::<*mut T>().map(|x| *x),
            _ => None
        }
    }
}

fn vm_type<'a, T: VMValue>(vm: &'a VM) -> &'a TcType {
    let t: T = unsafe { ::std::mem::uninitialized() };
    let typ = t.vm_type(vm);
    unsafe { ::std::mem::forget(t) }
    typ
}

fn make_type<'a, T: VMValue>(vm: &'a VM) -> TcType {
    let t: T = unsafe { ::std::mem::uninitialized() };
    let typ = t.make_type(vm);
    unsafe { ::std::mem::forget(t) }
    typ
}

trait Get<'a> {
    fn get_function(vm: &'a mut VM, name: &str) -> Option<Self>;
}
macro_rules! make_get(
    ($($args:ident),* -> $result:ident) => (
impl <'a, $($args : VMValue),*, $result: VMValue> Get<'a> for Callable<'a, ($($args),*), $result> {
    fn get_function(vm: &'a mut VM, name: &str) -> Option<Callable<'a, ($($args),*), $result>> {
        let value = match vm.get_global(name) {
            Some((function_ref, global)) => {
                match global.type_of() {
                    &FunctionType(ref args, ref return_type) => {
                        let mut arg_iter = args.iter();
                        let ok = $({
                            arg_iter.next().unwrap() == vm_type::<$args>(vm)
                            })&&*;
                        if arg_iter.next().is_none() && ok && **return_type == *vm_type::<$result>(vm) {
                            Some(FunctionRef { value: function_ref })
                        }
                        else {
                            None
                        }
                    }
                    _ => None
                }
            }
            None => None
        };
        match value {
            Some(value) => Some(Callable { vm: vm, value: value }),
            None => None
        }
    }
}
))

make_get!(A, B -> R)

impl <'a, T: VMValue, R: VMValue> Get<'a> for Callable<'a, (T,), R> {
    fn get_function(vm: &'a mut VM, name: &str) -> Option<Callable<'a, (T,), R>> {
        let value = match vm.get_global(name) {
            Some((function_ref, global)) => {
                match global.type_of() {
                    &FunctionType(ref args, ref return_type) => {
                        if args.len() == 1 {
                            let ok = args[0] == *vm_type::<T>(vm);
                            if ok && **return_type == *vm_type::<R>(vm) {
                                Some(FunctionRef { value: function_ref })
                            }
                            else {
                                None
                            }
                        }
                        else {
                            None
                        }
                    }
                    _ => None
                }
            }
            None => None
        };
        match value {
            Some(value) => Some(Callable { vm: vm, value: value }),
            None => None
        }
    }
}

pub struct Callable<'a, Args, R> {
    vm: &'a mut VM,
    value: FunctionRef<Args, R>
}
struct FunctionRef<Args, R> {
    value: uint
}

impl <Args, R> VMType for FunctionRef<Args, R> {
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType {
        vm.get_type::<|Args|:'static -> R>()
    }
}

impl <Args, R> VMValue for FunctionRef<Args, R> {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Function(self.value));
    }
    fn from_value(value: Value) -> Option<FunctionRef<Args, R>> {
        match value {
            Function(i) => Some(FunctionRef { value: i }),//TODO not type safe
            _ => None
        }
    }
}

impl <'a, A: VMValue, R: VMValue> Callable<'a, (A,), R> {
    pub fn call(&mut self, a: A) -> R {
        let mut vec = Vec::new();
        {
            let mut stack = StackFrame::new(&mut vec, 0, None);
            self.value.push(&mut stack);
            a.push(&mut stack);
            self.vm.execute(stack, &[CallGlobal(1)]);
        }
        vec.pop().and_then(|value| VMValue::from_value(value))
            .expect("Wrong type")
    }
}
impl <'a, A: VMValue, B: VMValue, R: VMValue> Callable<'a, (A, B), R> {
    pub fn call2(&mut self, a: A, b: B) -> R {
        let mut vec = Vec::new();
        {
            let mut stack = StackFrame::new(&mut vec, 0, None);
            self.value.push(&mut stack);
            a.push(&mut stack);
            b.push(&mut stack);
            self.vm.execute(stack, &[CallGlobal(2)]);
        }
        vec.pop().and_then(|value| VMValue::from_value(value))
            .expect("Wrong type")
    }
}

pub fn get_function<'a, T: Get<'a>>(vm: &'a mut VM, name: &str) -> Option<T> {
    Get::get_function(vm, name)
}

trait VMFunction {
    fn unpack_and_call(mut stack: StackFrame, f: Self);
}

impl <R: VMValue> VMFunction for fn () -> R {
    fn unpack_and_call(mut stack: StackFrame, f: fn () -> R) {
        let r = f();
        r.push(&mut stack);
    }
}
impl <R: VMValue> VMType for fn () -> R {
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType {
        vm.get_type::<fn () -> R>()
    }
    fn make_type(&self, vm: &VM) -> TcType {
        FunctionType(vec![], box make_type::<R>(vm))
    }
}

macro_rules! make_vm_function(
    ($($args:ident),*) => (
impl <$($args: VMValue),* , R: VMValue> VMType for fn ($($args),*) -> R {
    #[allow(non_snake_case)]
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType {
        vm.get_type::<fn ($($args),*) -> R>()
    }
    #[allow(non_snake_case)]
    fn make_type(&self, vm: &VM) -> TcType {
        FunctionType(vec![$(make_type::<$args>(vm)),*], box make_type::<R>(vm))
    }
}

impl <$($args : VMValue),*, R: VMValue> VMFunction for fn ($($args),*) -> R {
    #[allow(non_snake_case)]
    fn unpack_and_call(mut stack: StackFrame, f: fn ($($args),*) -> R) {
        let mut i = 0;
        $(let $args = {
            let x = VMValue::from_value(stack[i].clone()).unwrap();
            i += 1;
            x
        });*;
        drop(i);//Avoid unused warnings
        let r = f($($args),*);
        r.push(&mut stack);
    }
}
    )
)

make_vm_function!(A)
make_vm_function!(A, B)
make_vm_function!(A, B, C)
make_vm_function!(A, B, C, D)
make_vm_function!(A, B, C, D, E)
make_vm_function!(A, B, C, D, E, F)
make_vm_function!(A, B, C, D, E, F, G)

macro_rules! vm_function(
    ($func: expr) => ({
        fn wrapper(_: &VM, stack: StackFrame) {
            VMFunction::unpack_and_call(stack, $func)
        }
        wrapper
    })
)

macro_rules! define_function(
    ($vm: expr, $name: expr, $func: expr) => ({
        let vm = $vm;
        let (args, ret) = match $func.make_type(vm) {
            FunctionType(ref args, ref return_type) => (args.clone(), (**return_type).clone()),
            _ => fail!()
        };
        vm.extern_function($name, args, ret, vm_function!($func))
    })
)

#[cfg(test)]
mod tests {
    use super::{Get, Callable, VMType, VMFunction};

    use typecheck::FunctionType;
    use vm::{VM, load_script, StackFrame};
    use std::io::BufReader;

    #[test]
    fn call_function() {
        let s =
r"
fn add10(x: int) -> int {
    x + 10
}
fn mul(x: float, y: float) -> float {
    x * y
}
";
        let mut vm = VM::new();
        let mut buffer = BufReader::new(s.as_bytes());
        load_script(&mut vm, &mut buffer)
            .unwrap_or_else(|err| fail!("{}", err));
        {
            let mut f: Callable<(int,), int> = Get::get_function(&mut vm, "add10")
                .expect("No function");
            let result = f.call(2);
            assert_eq!(result, 12);
        }
        let mut f: Callable<(f64, f64), f64> = Get::get_function(&mut vm, "mul")
            .expect("No function");
        let result = f.call2(4., 5.);
        assert_eq!(result, 20.);
    }
    #[test]
    fn pass_userdata() {
        let s =
r"
fn id(x: Test) -> Test {
    x
}
";
        let mut vm = VM::new();
        fn test(test: *mut Test) -> bool {
            let test = unsafe { &mut *test };
            let x = test.x == 123;
            test.x *= 2;
            x
        }
        struct Test {
            x: int
        }
        vm.register_type::<Test>("Test")
            .unwrap_or_else(|_| fail!("Could not add type"));
        define_function!(&mut vm, "test", test);
        let mut buffer = BufReader::new(s.as_bytes());
        load_script(&mut vm, &mut buffer)
            .unwrap_or_else(|err| fail!("{}", err));

        let mut test = Test { x: 123 };
        {
            let mut f: Callable<(*mut Test,), *mut Test> = Get::get_function(&mut vm, "id")
                .expect("No function id");
            let result = f.call(&mut test);
            assert!(result == &mut test as *mut Test);
        }
        let mut f: Callable<(*mut Test,), bool> = Get::get_function(&mut vm, "test")
            .expect("No function test");
        let result = f.call(&mut test);
        assert!(result);
        assert_eq!(test.x, 123 * 2);
    }
}
<commit_msg>Modified the make_vm_function macro to also handle functions with no arguments<commit_after>use vm::{VM, Value, Int, Float, Function, Userdata, StackFrame, Data};
use typecheck::{TcType, Typed, FunctionType, unit_type_tc, bool_type_tc, int_type_tc, float_type_tc};
use compiler::CallGlobal;
use std::any::{Any, AnyRefExt};
use std::boxed::BoxAny;

pub trait VMType {
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType;
    fn make_type(&self, vm: &VM) -> TcType {
        self.vm_type(vm).clone()
    }
}
pub trait VMValue : VMType {
    fn push(self, stack: &mut StackFrame);
    fn from_value(value: Value) -> Option<Self>;
}
impl VMType for () {
    fn vm_type<'a>(&self, _: &'a VM) -> &'a TcType {
        &unit_type_tc
    }
}
impl VMValue for () {
    fn push(self, _: &mut StackFrame) {
    }
    fn from_value(_: Value) -> Option<()> {
        Some(())
    }
}

impl VMType for int {
    fn vm_type<'a>(&self, _: &'a VM) -> &'a TcType {
        &int_type_tc
    }
}
impl VMValue for int {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Int(self));
    }
    fn from_value(value: Value) -> Option<int> {
        match value {
            Int(i) => Some(i),
            _ => None
        }
    }
}
impl VMType for f64 {
    fn vm_type<'a>(&self, _: &'a VM) -> &'a TcType {
        &float_type_tc
    }
}
impl VMValue for f64 {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Float(self));
    }
    fn from_value(value: Value) -> Option<f64> {
        match value {
            Float(f) => Some(f),
            _ => None
        }
    }
}
impl VMType for bool {
    fn vm_type<'a>(&self, _: &'a VM) -> &'a TcType {
        &bool_type_tc
    }
}
impl VMValue for bool {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Int(if self { 1 } else { 0 }));
    }
    fn from_value(value: Value) -> Option<bool> {
        match value {
            Int(1) => Some(true),
            Int(0) => Some(false),
            _ => None
        }
    }
}
impl <T: 'static + BoxAny + Clone> VMType for Box<T> {
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType {
        vm.get_type::<T>()
    }
}
impl <T: 'static + BoxAny + Clone> VMValue for Box<T> {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Userdata(Data::new(self as Box<Any>)));
    }
    fn from_value(value: Value) -> Option<Box<T>> {
        match value {
            Userdata(v) => v.data.borrow().downcast_ref::<T>().map(|x| box x.clone()),
            _ => None
        }
    }
}
impl <T: 'static> VMType for *mut T {
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType {
        vm.get_type::<T>()
    }
}
impl <T: 'static> VMValue for *mut T {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Userdata(Data::new(box self as Box<Any>)));
    }
    fn from_value(value: Value) -> Option<*mut T> {
        match value {
            Userdata(v) => v.data.borrow().downcast_ref::<*mut T>().map(|x| *x),
            _ => None
        }
    }
}

fn vm_type<'a, T: VMValue>(vm: &'a VM) -> &'a TcType {
    let t: T = unsafe { ::std::mem::uninitialized() };
    let typ = t.vm_type(vm);
    unsafe { ::std::mem::forget(t) }
    typ
}

fn make_type<'a, T: VMValue>(vm: &'a VM) -> TcType {
    let t: T = unsafe { ::std::mem::uninitialized() };
    let typ = t.make_type(vm);
    unsafe { ::std::mem::forget(t) }
    typ
}

trait Get<'a> {
    fn get_function(vm: &'a mut VM, name: &str) -> Option<Self>;
}
macro_rules! make_get(
    ($($args:ident),* -> $result:ident) => (
impl <'a, $($args : VMValue),*, $result: VMValue> Get<'a> for Callable<'a, ($($args),*), $result> {
    fn get_function(vm: &'a mut VM, name: &str) -> Option<Callable<'a, ($($args),*), $result>> {
        let value = match vm.get_global(name) {
            Some((function_ref, global)) => {
                match global.type_of() {
                    &FunctionType(ref args, ref return_type) => {
                        let mut arg_iter = args.iter();
                        let ok = $({
                            arg_iter.next().unwrap() == vm_type::<$args>(vm)
                            })&&*;
                        if arg_iter.next().is_none() && ok && **return_type == *vm_type::<$result>(vm) {
                            Some(FunctionRef { value: function_ref })
                        }
                        else {
                            None
                        }
                    }
                    _ => None
                }
            }
            None => None
        };
        match value {
            Some(value) => Some(Callable { vm: vm, value: value }),
            None => None
        }
    }
}
))

make_get!(A, B -> R)

impl <'a, T: VMValue, R: VMValue> Get<'a> for Callable<'a, (T,), R> {
    fn get_function(vm: &'a mut VM, name: &str) -> Option<Callable<'a, (T,), R>> {
        let value = match vm.get_global(name) {
            Some((function_ref, global)) => {
                match global.type_of() {
                    &FunctionType(ref args, ref return_type) => {
                        if args.len() == 1 {
                            let ok = args[0] == *vm_type::<T>(vm);
                            if ok && **return_type == *vm_type::<R>(vm) {
                                Some(FunctionRef { value: function_ref })
                            }
                            else {
                                None
                            }
                        }
                        else {
                            None
                        }
                    }
                    _ => None
                }
            }
            None => None
        };
        match value {
            Some(value) => Some(Callable { vm: vm, value: value }),
            None => None
        }
    }
}

pub struct Callable<'a, Args, R> {
    vm: &'a mut VM,
    value: FunctionRef<Args, R>
}
struct FunctionRef<Args, R> {
    value: uint
}

impl <Args, R> VMType for FunctionRef<Args, R> {
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType {
        vm.get_type::<|Args|:'static -> R>()
    }
}

impl <Args, R> VMValue for FunctionRef<Args, R> {
    fn push(self, stack: &mut StackFrame) {
        stack.push(Function(self.value));
    }
    fn from_value(value: Value) -> Option<FunctionRef<Args, R>> {
        match value {
            Function(i) => Some(FunctionRef { value: i }),//TODO not type safe
            _ => None
        }
    }
}

impl <'a, A: VMValue, R: VMValue> Callable<'a, (A,), R> {
    pub fn call(&mut self, a: A) -> R {
        let mut vec = Vec::new();
        {
            let mut stack = StackFrame::new(&mut vec, 0, None);
            self.value.push(&mut stack);
            a.push(&mut stack);
            self.vm.execute(stack, &[CallGlobal(1)]);
        }
        vec.pop().and_then(|value| VMValue::from_value(value))
            .expect("Wrong type")
    }
}
impl <'a, A: VMValue, B: VMValue, R: VMValue> Callable<'a, (A, B), R> {
    pub fn call2(&mut self, a: A, b: B) -> R {
        let mut vec = Vec::new();
        {
            let mut stack = StackFrame::new(&mut vec, 0, None);
            self.value.push(&mut stack);
            a.push(&mut stack);
            b.push(&mut stack);
            self.vm.execute(stack, &[CallGlobal(2)]);
        }
        vec.pop().and_then(|value| VMValue::from_value(value))
            .expect("Wrong type")
    }
}

pub fn get_function<'a, T: Get<'a>>(vm: &'a mut VM, name: &str) -> Option<T> {
    Get::get_function(vm, name)
}

trait VMFunction {
    fn unpack_and_call(mut stack: StackFrame, f: Self);
}

macro_rules! make_vm_function(
    ($($args:ident),*) => (
impl <$($args: VMValue,)* R: VMValue> VMType for fn ($($args),*) -> R {
    #[allow(non_snake_case)]
    fn vm_type<'a>(&self, vm: &'a VM) -> &'a TcType {
        vm.get_type::<fn ($($args),*) -> R>()
    }
    #[allow(non_snake_case)]
    fn make_type(&self, vm: &VM) -> TcType {
        FunctionType(vec![$(make_type::<$args>(vm)),*], box make_type::<R>(vm))
    }
}

impl <$($args : VMValue,)* R: VMValue> VMFunction for fn ($($args),*) -> R {
    #[allow(non_snake_case)]
    fn unpack_and_call(mut stack: StackFrame, f: fn ($($args),*) -> R) {
        let mut i = 0u;
        $(let $args = {
            let x = VMValue::from_value(stack[i].clone()).unwrap();
            i += 1;
            x
        });*;
        drop(i);//Avoid unused warnings
        let r = f($($args),*);
        r.push(&mut stack);
    }
}
    )
)

make_vm_function!()
make_vm_function!(A)
make_vm_function!(A, B)
make_vm_function!(A, B, C)
make_vm_function!(A, B, C, D)
make_vm_function!(A, B, C, D, E)
make_vm_function!(A, B, C, D, E, F)
make_vm_function!(A, B, C, D, E, F, G)

macro_rules! vm_function(
    ($func: expr) => ({
        fn wrapper(_: &VM, stack: StackFrame) {
            VMFunction::unpack_and_call(stack, $func)
        }
        wrapper
    })
)

macro_rules! define_function(
    ($vm: expr, $name: expr, $func: expr) => ({
        let vm = $vm;
        let (args, ret) = match $func.make_type(vm) {
            FunctionType(ref args, ref return_type) => (args.clone(), (**return_type).clone()),
            _ => fail!()
        };
        vm.extern_function($name, args, ret, vm_function!($func))
    })
)

#[cfg(test)]
mod tests {
    use super::{Get, Callable, VMType, VMFunction};

    use typecheck::FunctionType;
    use vm::{VM, load_script, StackFrame};
    use std::io::BufReader;

    #[test]
    fn call_function() {
        let s =
r"
fn add10(x: int) -> int {
    x + 10
}
fn mul(x: float, y: float) -> float {
    x * y
}
";
        let mut vm = VM::new();
        let mut buffer = BufReader::new(s.as_bytes());
        load_script(&mut vm, &mut buffer)
            .unwrap_or_else(|err| fail!("{}", err));
        {
            let mut f: Callable<(int,), int> = Get::get_function(&mut vm, "add10")
                .expect("No function");
            let result = f.call(2);
            assert_eq!(result, 12);
        }
        let mut f: Callable<(f64, f64), f64> = Get::get_function(&mut vm, "mul")
            .expect("No function");
        let result = f.call2(4., 5.);
        assert_eq!(result, 20.);
    }
    #[test]
    fn pass_userdata() {
        let s =
r"
fn id(x: Test) -> Test {
    x
}
";
        let mut vm = VM::new();
        fn test(test: *mut Test) -> bool {
            let test = unsafe { &mut *test };
            let x = test.x == 123;
            test.x *= 2;
            x
        }
        struct Test {
            x: int
        }
        vm.register_type::<Test>("Test")
            .unwrap_or_else(|_| fail!("Could not add type"));
        define_function!(&mut vm, "test", test);
        let mut buffer = BufReader::new(s.as_bytes());
        load_script(&mut vm, &mut buffer)
            .unwrap_or_else(|err| fail!("{}", err));

        let mut test = Test { x: 123 };
        {
            let mut f: Callable<(*mut Test,), *mut Test> = Get::get_function(&mut vm, "id")
                .expect("No function id");
            let result = f.call(&mut test);
            assert!(result == &mut test as *mut Test);
        }
        let mut f: Callable<(*mut Test,), bool> = Get::get_function(&mut vm, "test")
            .expect("No function test");
        let result = f.call(&mut test);
        assert!(result);
        assert_eq!(test.x, 123 * 2);
    }
}
<|endoftext|>


<commit_before>//! The entry point.
#![crate_type = "bin"]
#![deny(missing_docs)]
#![deny(warnings)]

#![feature(core)]
#![feature(collections)]
#![feature(hash)]
#![feature(io)]
#![feature(path)]
#![feature(slicing_syntax)]
#![feature(std_misc)]
#![feature(test)]
#![feature(unboxed_closures)]
#![feature(unsafe_destructor)]

extern crate gl;
#[macro_use]
extern crate log;
extern crate nalgebra;
extern crate ncollide_entities;
extern crate ncollide_queries;
extern crate noise;
extern crate opencl;
extern crate rand;
extern crate sdl2;
extern crate "sdl2-sys" as sdl2_sys;
extern crate test;
extern crate time;
extern crate yaglw;

mod camera;
mod client;
mod client_thread;
mod client_update;
mod color;
mod common;
mod cube_shell;
mod fontloader;
mod id_allocator;
mod in_progress_terrain;
mod init;
mod interval_timer;
mod light;
mod loader;
mod logger;
mod lod_map;
mod main;
mod mob;
mod octree;
mod opencl_context;
mod physics;
mod player;
mod process_event;
mod range_abs;
mod render;
mod server;
mod server_thread;
mod server_update;
mod shaders;
mod stopwatch;
mod sun;
mod surroundings_iter;
mod surroundings_loader;
mod terrain;
mod ttf;
mod update;
mod vertex;
mod view;
mod view_thread;
mod view_update;

#[allow(dead_code)]
fn main() {
  return main::main();
}
<commit_msg>rustup<commit_after>//! The entry point.
#![crate_type = "bin"]
#![deny(missing_docs)]
#![deny(warnings)]

#![feature(core)]
#![feature(collections)]
#![feature(hash)]
#![feature(io)]
#![feature(path)]
#![feature(std_misc)]
#![feature(test)]
#![feature(unboxed_closures)]
#![feature(unsafe_destructor)]

extern crate gl;
#[macro_use]
extern crate log;
extern crate nalgebra;
extern crate ncollide_entities;
extern crate ncollide_queries;
extern crate noise;
extern crate opencl;
extern crate rand;
extern crate sdl2;
extern crate "sdl2-sys" as sdl2_sys;
extern crate test;
extern crate time;
extern crate yaglw;

mod camera;
mod client;
mod client_thread;
mod client_update;
mod color;
mod common;
mod cube_shell;
mod fontloader;
mod id_allocator;
mod in_progress_terrain;
mod init;
mod interval_timer;
mod light;
mod loader;
mod logger;
mod lod_map;
mod main;
mod mob;
mod octree;
mod opencl_context;
mod physics;
mod player;
mod process_event;
mod range_abs;
mod render;
mod server;
mod server_thread;
mod server_update;
mod shaders;
mod stopwatch;
mod sun;
mod surroundings_iter;
mod surroundings_loader;
mod terrain;
mod ttf;
mod update;
mod vertex;
mod view;
mod view_thread;
mod view_update;

#[allow(dead_code)]
fn main() {
  return main::main();
}
<|endoftext|>


<commit_before>use ffi::*;
use element::Element;
use element::ElementT;
use util::*;

/**
GstBin is an element that can contain other GstElement, allowing them to be managed as a group. Pads from the child elements can be ghosted to the bin, see GstGhostPad. This makes the bin look like any other elements and enables creation of higher-level abstraction elements.

A new GstBin is created with gst_bin_new(). Use a GstPipeline instead if you want to create a toplevel bin because a normal bin doesn't have a bus or handle clock distribution of its own.

After the bin has been created you will typically add elements to it with gst_bin_add(). You can remove elements with gst_bin_remove().

An element can be retrieved from a bin with gst_bin_get_by_name(), using the elements name. gst_bin_get_by_name_recurse_up() is mainly used for internal purposes and will query the parent bins when the element is not found in the current bin.

An iterator of elements in a bin can be retrieved with gst_bin_iterate_elements(). Various other iterators exist to retrieve the elements in a bin.

gst_object_unref() is used to drop your reference to the bin.

The “element-added” signal is fired whenever a new element is added to the bin. Likewise the “element-removed” signal is fired whenever an element is removed from the bin.
Notes

A GstBin internally intercepts every GstMessage posted by its children and implements the following default behaviour for each of them:

GST_MESSAGE_EOS

This message is only posted by sinks in the PLAYING state. If all sinks posted the EOS message, this bin will post and EOS message upwards.

GST_MESSAGE_SEGMENT_START

just collected and never forwarded upwards. The messages are used to decide when all elements have completed playback of their segment.

GST_MESSAGE_SEGMENT_DONE

Is posted by GstBin when all elements that posted a SEGMENT_START have posted a SEGMENT_DONE.

GST_MESSAGE_DURATION_CHANGED

Is posted by an element that detected a change in the stream duration. The default bin behaviour is to clear any cached duration values so that the next duration query will perform a full duration recalculation. The duration change is posted to the application so that it can refetch the new duration with a duration query. Note that these messages can be posted before the bin is prerolled, in which case the duration query might fail.

GST_MESSAGE_CLOCK_LOST

This message is posted by an element when it can no longer provide a clock. The default bin behaviour is to check if the lost clock was the one provided by the bin. If so and the bin is currently in the PLAYING state, the message is forwarded to the bin parent. This message is also generated when a clock provider is removed from the bin. If this message is received by the application, it should PAUSE the pipeline and set it back to PLAYING to force a new clock distribution.

GST_MESSAGE_CLOCK_PROVIDE

This message is generated when an element can provide a clock. This mostly happens when a new clock provider is added to the bin. The default behaviour of the bin is to mark the currently selected clock as dirty, which will perform a clock recalculation the next time the bin is asked to provide a clock. This message is never sent tot the application but is forwarded to the parent of the bin.

OTHERS

posted upwards.

A GstBin implements the following default behaviour for answering to a GstQuery:

GST_QUERY_DURATION

If the query has been asked before with the same format and the bin is a toplevel bin (ie. has no parent), use the cached previous value. If no previous value was cached, the query is sent to all sink elements in the bin and the MAXIMUM of all values is returned. If the bin is a toplevel bin the value is cached. If no sinks are available in the bin, the query fails.

GST_QUERY_POSITION

The query is sent to all sink elements in the bin and the MAXIMUM of all values is returned. If no sinks are available in the bin, the query fails.

OTHERS

the query is forwarded to all sink elements, the result of the first sink that answers the query successfully is returned. If no sink is in the bin, the query fails.

A GstBin will by default forward any event sent to it to all sink elements. If all the sinks return TRUE, the bin will also return TRUE, else FALSE is returned. If no sinks are in the bin, the event handler will return TRUE.*/
pub struct Bin{
    bin: Element
}

unsafe impl Sync for Bin {}
unsafe impl Send for Bin {}

impl Bin{
    /// Creates a new bin with the given name.
    pub fn new(name: &str) -> Option<Bin>{
        unsafe{
            let bin = gst_bin_new(to_c_str!(name));
            if bin != ptr::null_mut(){
	            gst_object_ref_sink(mem::transmute(bin));
	            Bin::new_from_gst_bin(bin as *mut GstBin)
	        }else{
	            None
	        }
        }
    }

    /// Creates a new bin from an already existing raw pointer to a GstBin.
    /// The passed element has to be fully referenced
    pub unsafe fn new_from_gst_bin(element: *mut GstBin) -> Option<Bin>{
        match Element::new_from_gst_element(element as *mut GstElement){
            Some(element) => Some( Bin{ bin: element } ),
            None => None
        }
    }
}

pub trait BinT: ElementT{
    fn as_bin(&self) -> &Bin;
    fn as_bin_mut(&mut self) -> &mut Bin;

    fn to_bin(&self) -> Bin{
        Bin{bin: self.to_element()}
    }

    /// Adds the given element to the bin. Sets the element's parent,
    /// and thus adds a reference.
    ///
    /// If the element's pads are linked to other pads, the pads will
    /// be unlinked before the element is added to the bin.
    ///
    /// > When you add an element to an already-running pipeline, you
    /// > will have to take care to set the state of the newly-added
    /// > element to the desired state (usually PLAYING or PAUSED, same
    /// > you set the pipeline to originally) with Element::set_state(),
    /// > or use gst_element_sync_state_with_parent(). The bin or pipeline
    /// > will not take care of this for you.
    fn add<E:ElementT>(&mut self, element: E) -> bool{
        self.as_bin_mut().add(element)
    }

    /// Remove the element from its associated bin.
    ///
    /// If the element's pads are linked to other pads, the pads will be
    /// unlinked before the element is removed from the bin.
    fn remove(&mut self, element: &ElementT) -> bool{
        self.as_bin_mut().remove(element)
    }

    /// Get the element with the given name from this bin.
    ///
    /// Returns None if no element with the given name is found in the bin.
    fn get_by_name(&self, name: &str) -> Option<Element>{
        self.as_bin().get_by_name(name)
    }

    /// Query bin for the current latency using and reconfigures this latency
    /// to all the elements with a LATENCY event.
	///
	/// This method is typically called on the pipeline when a
	/// GST_MESSAGE_LATENCY is posted on the bus.
	///
	/// This function simply emits the 'do-latency' signal so any custom
	/// latency calculations will be performed.
    fn recalculate_latency(&self) -> bool{
        self.as_bin().recalculate_latency()
    }

    /// If set to true, the bin will handle asynchronous state changes.
    /// This should be used only if the bin subclass is modifying the state
    /// of its children on its own
    fn set_async_handling(&self, async: bool){
        self.as_bin().set_async_handling(async)
    }

    /// Forward all children messages, even those that would normally be
    /// filtered by the bin. This can be interesting when one wants to be
    /// notified of the EOS state of individual elements, for example.
	///
	/// The messages are converted to an ELEMENT message with the bin as the
	/// source. The structure of the message is named 'GstBinForwarded' and
	/// contains a field named 'message' of type GST_TYPE_MESSAGE that
	/// contains the original forwarded message.
    fn set_message_forward(&self, forward: bool){
        self.as_bin().set_message_forward(forward)
    }

    /// Returns a const raw pointer to the internal GstElement
    unsafe fn gst_bin(&self) -> *const GstBin{
        self.as_bin().gst_bin()
    }

    /// Returns a mut raw pointer to the internal GstElement
    unsafe fn gst_bin_mut(&mut self) -> *mut GstBin{
        self.as_bin_mut().gst_bin_mut()
    }
}

impl BinT for Bin{
    fn add<E>(&mut self, element: E) -> bool
    	where E:ElementT{
        unsafe{
            gst_bin_add(self.gst_bin_mut(), element.transfer() as *mut GstElement) == 1
        }
    }

    fn as_bin(&self) -> &Bin{
        self
    }

    fn as_bin_mut(&mut self) -> &mut Bin{
        self
    }

    fn remove(&mut self, element: &ElementT) -> bool{
        unsafe{
            gst_bin_remove(self.gst_bin_mut(), mem::transmute(element.gst_element())) == 1
        }
    }

    fn get_by_name(&self, name: &str) -> Option<Element>{
        unsafe{
            let element = gst_bin_get_by_name(self.gst_bin() as *mut GstBin, to_c_str!(name));
            Element::new_from_gst_element(element)
        }
    }

    fn recalculate_latency(&self) -> bool{
        unsafe{
            gst_bin_recalculate_latency(self.gst_bin() as *mut GstBin) == 1
        }
    }

    fn set_async_handling(&self, async: bool){
        self.bin.set("async-handling", async);
    }

    fn set_message_forward(&self, forward: bool){
        self.bin.set("message-forward", forward);
    }

    unsafe fn gst_bin(&self) -> *const GstBin{
        self.bin.gst_element() as *const GstBin
    }

    unsafe fn gst_bin_mut(&mut self) -> *mut GstBin{
        self.bin.gst_element() as *mut GstBin
    }
}

impl<B:BinT> ElementT for B{
    fn as_element(&self) -> &Element{
        &self.as_bin().bin
    }

    fn as_element_mut(&mut self) -> &mut Element{
        &mut self.as_bin_mut().bin
    }
}

impl ::Transfer for Bin{
    unsafe fn transfer(self) -> *mut GstElement{
        self.bin.transfer()
    }
}<commit_msg>bin: likewise<commit_after>use ffi::*;
use element::Element;
use element::ElementT;
use util::*;

/**
GstBin is an element that can contain other GstElement, allowing them to be managed as a group. Pads from the child elements can be ghosted to the bin, see GstGhostPad. This makes the bin look like any other elements and enables creation of higher-level abstraction elements.

A new GstBin is created with gst_bin_new(). Use a GstPipeline instead if you want to create a toplevel bin because a normal bin doesn't have a bus or handle clock distribution of its own.

After the bin has been created you will typically add elements to it with gst_bin_add(). You can remove elements with gst_bin_remove().

An element can be retrieved from a bin with gst_bin_get_by_name(), using the elements name. gst_bin_get_by_name_recurse_up() is mainly used for internal purposes and will query the parent bins when the element is not found in the current bin.

An iterator of elements in a bin can be retrieved with gst_bin_iterate_elements(). Various other iterators exist to retrieve the elements in a bin.

gst_object_unref() is used to drop your reference to the bin.

The “element-added” signal is fired whenever a new element is added to the bin. Likewise the “element-removed” signal is fired whenever an element is removed from the bin.
Notes

A GstBin internally intercepts every GstMessage posted by its children and implements the following default behaviour for each of them:

GST_MESSAGE_EOS

This message is only posted by sinks in the PLAYING state. If all sinks posted the EOS message, this bin will post and EOS message upwards.

GST_MESSAGE_SEGMENT_START

just collected and never forwarded upwards. The messages are used to decide when all elements have completed playback of their segment.

GST_MESSAGE_SEGMENT_DONE

Is posted by GstBin when all elements that posted a SEGMENT_START have posted a SEGMENT_DONE.

GST_MESSAGE_DURATION_CHANGED

Is posted by an element that detected a change in the stream duration. The default bin behaviour is to clear any cached duration values so that the next duration query will perform a full duration recalculation. The duration change is posted to the application so that it can refetch the new duration with a duration query. Note that these messages can be posted before the bin is prerolled, in which case the duration query might fail.

GST_MESSAGE_CLOCK_LOST

This message is posted by an element when it can no longer provide a clock. The default bin behaviour is to check if the lost clock was the one provided by the bin. If so and the bin is currently in the PLAYING state, the message is forwarded to the bin parent. This message is also generated when a clock provider is removed from the bin. If this message is received by the application, it should PAUSE the pipeline and set it back to PLAYING to force a new clock distribution.

GST_MESSAGE_CLOCK_PROVIDE

This message is generated when an element can provide a clock. This mostly happens when a new clock provider is added to the bin. The default behaviour of the bin is to mark the currently selected clock as dirty, which will perform a clock recalculation the next time the bin is asked to provide a clock. This message is never sent tot the application but is forwarded to the parent of the bin.

OTHERS

posted upwards.

A GstBin implements the following default behaviour for answering to a GstQuery:

GST_QUERY_DURATION

If the query has been asked before with the same format and the bin is a toplevel bin (ie. has no parent), use the cached previous value. If no previous value was cached, the query is sent to all sink elements in the bin and the MAXIMUM of all values is returned. If the bin is a toplevel bin the value is cached. If no sinks are available in the bin, the query fails.

GST_QUERY_POSITION

The query is sent to all sink elements in the bin and the MAXIMUM of all values is returned. If no sinks are available in the bin, the query fails.

OTHERS

the query is forwarded to all sink elements, the result of the first sink that answers the query successfully is returned. If no sink is in the bin, the query fails.

A GstBin will by default forward any event sent to it to all sink elements. If all the sinks return TRUE, the bin will also return TRUE, else FALSE is returned. If no sinks are in the bin, the event handler will return TRUE.*/
pub struct Bin{
    bin: Element
}

unsafe impl Sync for Bin {}
unsafe impl Send for Bin {}

impl Bin{
    /// Creates a new bin with the given name.
    pub fn new(name: &str) -> Option<Bin>{
        unsafe{
            let name = if name != "" {
                to_c_str!(name)
            } else {
                ptr::null()
            };
            let bin = gst_bin_new(name);
            if bin != ptr::null_mut(){
	            gst_object_ref_sink(mem::transmute(bin));
	            Bin::new_from_gst_bin(bin as *mut GstBin)
	        }else{
	            None
	        }
        }
    }

    /// Creates a new bin from an already existing raw pointer to a GstBin.
    /// The passed element has to be fully referenced
    pub unsafe fn new_from_gst_bin(element: *mut GstBin) -> Option<Bin>{
        match Element::new_from_gst_element(element as *mut GstElement){
            Some(element) => Some( Bin{ bin: element } ),
            None => None
        }
    }
}

pub trait BinT: ElementT{
    fn as_bin(&self) -> &Bin;
    fn as_bin_mut(&mut self) -> &mut Bin;

    fn to_bin(&self) -> Bin{
        Bin{bin: self.to_element()}
    }

    /// Adds the given element to the bin. Sets the element's parent,
    /// and thus adds a reference.
    ///
    /// If the element's pads are linked to other pads, the pads will
    /// be unlinked before the element is added to the bin.
    ///
    /// > When you add an element to an already-running pipeline, you
    /// > will have to take care to set the state of the newly-added
    /// > element to the desired state (usually PLAYING or PAUSED, same
    /// > you set the pipeline to originally) with Element::set_state(),
    /// > or use gst_element_sync_state_with_parent(). The bin or pipeline
    /// > will not take care of this for you.
    fn add<E:ElementT>(&mut self, element: E) -> bool{
        self.as_bin_mut().add(element)
    }

    /// Remove the element from its associated bin.
    ///
    /// If the element's pads are linked to other pads, the pads will be
    /// unlinked before the element is removed from the bin.
    fn remove(&mut self, element: &ElementT) -> bool{
        self.as_bin_mut().remove(element)
    }

    /// Get the element with the given name from this bin.
    ///
    /// Returns None if no element with the given name is found in the bin.
    fn get_by_name(&self, name: &str) -> Option<Element>{
        self.as_bin().get_by_name(name)
    }

    /// Query bin for the current latency using and reconfigures this latency
    /// to all the elements with a LATENCY event.
	///
	/// This method is typically called on the pipeline when a
	/// GST_MESSAGE_LATENCY is posted on the bus.
	///
	/// This function simply emits the 'do-latency' signal so any custom
	/// latency calculations will be performed.
    fn recalculate_latency(&self) -> bool{
        self.as_bin().recalculate_latency()
    }

    /// If set to true, the bin will handle asynchronous state changes.
    /// This should be used only if the bin subclass is modifying the state
    /// of its children on its own
    fn set_async_handling(&self, async: bool){
        self.as_bin().set_async_handling(async)
    }

    /// Forward all children messages, even those that would normally be
    /// filtered by the bin. This can be interesting when one wants to be
    /// notified of the EOS state of individual elements, for example.
	///
	/// The messages are converted to an ELEMENT message with the bin as the
	/// source. The structure of the message is named 'GstBinForwarded' and
	/// contains a field named 'message' of type GST_TYPE_MESSAGE that
	/// contains the original forwarded message.
    fn set_message_forward(&self, forward: bool){
        self.as_bin().set_message_forward(forward)
    }

    /// Returns a const raw pointer to the internal GstElement
    unsafe fn gst_bin(&self) -> *const GstBin{
        self.as_bin().gst_bin()
    }

    /// Returns a mut raw pointer to the internal GstElement
    unsafe fn gst_bin_mut(&mut self) -> *mut GstBin{
        self.as_bin_mut().gst_bin_mut()
    }
}

impl BinT for Bin{
    fn add<E>(&mut self, element: E) -> bool
    	where E:ElementT{
        unsafe{
            gst_bin_add(self.gst_bin_mut(), element.transfer() as *mut GstElement) == 1
        }
    }

    fn as_bin(&self) -> &Bin{
        self
    }

    fn as_bin_mut(&mut self) -> &mut Bin{
        self
    }

    fn remove(&mut self, element: &ElementT) -> bool{
        unsafe{
            gst_bin_remove(self.gst_bin_mut(), mem::transmute(element.gst_element())) == 1
        }
    }

    fn get_by_name(&self, name: &str) -> Option<Element>{
        unsafe{
            let element = gst_bin_get_by_name(self.gst_bin() as *mut GstBin, to_c_str!(name));
            Element::new_from_gst_element(element)
        }
    }

    fn recalculate_latency(&self) -> bool{
        unsafe{
            gst_bin_recalculate_latency(self.gst_bin() as *mut GstBin) == 1
        }
    }

    fn set_async_handling(&self, async: bool){
        self.bin.set("async-handling", async);
    }

    fn set_message_forward(&self, forward: bool){
        self.bin.set("message-forward", forward);
    }

    unsafe fn gst_bin(&self) -> *const GstBin{
        self.bin.gst_element() as *const GstBin
    }

    unsafe fn gst_bin_mut(&mut self) -> *mut GstBin{
        self.bin.gst_element() as *mut GstBin
    }
}

impl<B:BinT> ElementT for B{
    fn as_element(&self) -> &Element{
        &self.as_bin().bin
    }

    fn as_element_mut(&mut self) -> &mut Element{
        &mut self.as_bin_mut().bin
    }
}

impl ::Transfer for Bin{
    unsafe fn transfer(self) -> *mut GstElement{
        self.bin.transfer()
    }
}<|endoftext|>