<commit_before>#![deny(unused_import_braces,
        unused_comparisons, unused_must_use,
        unused_variables, non_shorthand_field_patterns,
        unreachable_code)]

extern crate trie;
extern crate sputnikvm;
extern crate sha3;
extern crate block;
extern crate rlp;

use sputnikvm::{H256, U256, M256, Address};
use sputnikvm::vm::{self, ValidTransaction, HeaderParams, Memory, TransactionVM, VM,
                    AccountCommitment, Patch, SeqMemory, AccountState};
use sputnikvm::vm::errors::{PreExecutionError, RequireError};
use sha3::{Keccak256, Digest};
use trie::{Trie, SecureTrie, FixedSecureTrie, DatabaseGuard, MemoryDatabase, MemoryDatabaseGuard, Database, DatabaseOwned};
use block::{Account, Transaction};
use std::collections::HashMap;
use std::cmp::min;

pub struct Stateful<D> {
    database: D,
    root: H256,
}

impl<D> Stateful<D> {
    pub fn new(database: D, root: H256) -> Self {
        Self {
            database,
            root
        }
    }
}

impl<D: Default> Default for Stateful<D> {
    fn default() -> Self {
        Self {
            database: D::default(),
            root: MemoryDatabase::new().create_empty().root(),
        }
    }
}

impl<D: DatabaseOwned> Stateful<D> {
    fn is_empty_hash(hash: H256) -> bool {
        hash == H256::from(Keccak256::digest(&[]).as_slice())
    }

    pub fn code(&self, hash: H256) -> Option<Vec<u8>> {
        let code_hashes = self.database.create_guard();

        if Self::is_empty_hash(hash) {
            Some(Vec::new())
        } else {
            code_hashes.get(hash)
        }
    }

    pub fn call<M: Memory + Default>(
        &self, transaction: ValidTransaction, block: HeaderParams,
        patch: &'static Patch, most_recent_block_hashes: &[H256]
    ) -> TransactionVM<M> {
        assert!(U256::from(most_recent_block_hashes.len()) >=
                min(block.number, U256::from(256)));

        let mut vm = TransactionVM::new(transaction, block.clone(), patch);
        let state = self.database.create_fixed_secure_trie(self.root);
        let code_hashes = self.database.create_guard();

        loop {
            match vm.fire() {
                Ok(()) => break,
                Err(RequireError::Account(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            vm.commit_account(AccountCommitment::Full {
                                nonce: account.nonce,
                                address: address,
                                balance: account.balance,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountCode(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            vm.commit_account(AccountCommitment::Code {
                                address: address,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountStorage(address, index)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let storage = self.database.create_fixed_secure_trie(account.storage_root);
                            let value = storage.get(&H256::from(index)).unwrap_or(M256::zero());

                            vm.commit_account(AccountCommitment::Storage {
                                address: address,
                                index, value
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::Blockhash(number)) => {
                    let index = (block.number - number).as_usize();
                    vm.commit_blockhash(number, most_recent_block_hashes[index]).unwrap();
                },
            }
        }

        vm
    }

    pub fn transit(
        &mut self, accounts: &[vm::Account]
    ) {
        let mut state = self.database.create_fixed_secure_trie(self.root);
        let mut code_hashes = self.database.create_guard();

        for account in accounts {
            match account.clone() {
                vm::Account::Full {
                    nonce, address, balance, changing_storage, code
                } => {
                    let changing_storage: HashMap<U256, M256> = changing_storage.into();

                    let mut account: Account = state.get(&address).unwrap();

                    let mut storage_trie = self.database.create_fixed_secure_trie(account.storage_root);
                    for (key, value) in changing_storage {
                        if value == M256::zero() {
                            storage_trie.remove(&H256::from(key));
                        } else {
                            storage_trie.insert(H256::from(key), value);
                        }
                    }

                    account.balance = balance;
                    account.nonce = nonce;
                    account.storage_root = storage_trie.root();
                    assert!(account.code_hash == H256::from(Keccak256::digest(&code).as_slice()));

                    state.insert(address, account);
                },
                vm::Account::IncreaseBalance(address, value) => {
                    let mut account: Account = state.get(&address).unwrap();

                    account.balance = account.balance + value;

                    state.insert(address, account);
                },
                vm::Account::DecreaseBalance(address, value) => {
                    let mut account: Account = state.get(&address).unwrap();

                    account.balance = account.balance - value;

                    state.insert(address, account);
                },
                vm::Account::Create {
                    nonce, address, balance, storage, code, exists
                } => {
                    if !exists {
                        state.remove(&address);
                    } else {
                        let storage: HashMap<U256, M256> = storage.into();

                        let mut storage_trie = self.database.create_fixed_secure_empty();
                        for (key, value) in storage {
                            if value == M256::zero() {
                                storage_trie.remove(&H256::from(key));
                            } else {
                                storage_trie.insert(H256::from(key), value);
                            }
                        }

                        let code_hash = H256::from(Keccak256::digest(&code).as_slice());
                        code_hashes.set(code_hash, code);

                        let account = Account {
                            nonce: nonce,
                            balance: balance,
                            storage_root: storage_trie.root(),
                            code_hash
                        };

                        state.insert(address, account);
                    }
                },
            }
        }

        self.root = state.root();
    }

    pub fn execute<M: Memory + Default>(
        &mut self, transaction: ValidTransaction, block: HeaderParams,
        patch: &'static Patch, most_recent_block_hashes: &[H256]
    ) -> TransactionVM<M> {
        let vm = self.call(transaction, block, patch, most_recent_block_hashes);
        let mut accounts = Vec::new();
        for account in vm.accounts() {
            accounts.push(account.clone());
        }
        self.transit(&accounts);
        vm
    }

    pub fn to_valid(
        &self, transaction: Transaction, patch: &'static Patch
    ) -> Result<ValidTransaction, PreExecutionError> {
        let state = self.database.create_fixed_secure_trie(self.root);
        let code_hashes = self.database.create_guard();
        let mut account_state = AccountState::default();

        loop {
            match ValidTransaction::from_transaction(&transaction, &account_state, patch) {
                Ok(val) => return val,
                Err(RequireError::Account(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            account_state.commit(AccountCommitment::Full {
                                nonce: account.nonce,
                                address: address,
                                balance: account.balance,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            account_state.commit(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountCode(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            account_state.commit(AccountCommitment::Code {
                                address: address,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            account_state.commit(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountStorage(address, index)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let storage = self.database.create_fixed_secure_trie(account.storage_root);
                            let value = storage.get(&H256::from(index)).unwrap_or(M256::zero());

                            account_state.commit(AccountCommitment::Storage {
                                address: address,
                                index, value
                            }).unwrap();
                        },
                        None => {
                            account_state.commit(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::Blockhash(_)) => {
                    panic!()
                },
            }
        }
    }

    pub fn root(&self) -> H256 {
        self.root
    }

    pub fn state_of<'a>(&'a self, root: H256) -> FixedSecureTrie<<D as Database<'a>>::Guard, Address, Account> {
        self.database.create_fixed_secure_trie::<Address, Account>(root)
    }

    pub fn state<'a>(&'a self) -> FixedSecureTrie<<D as Database<'a>>::Guard, Address, Account> {
        self.state_of(self.root())
    }

    pub fn storage_state_of<'a>(&'a self, root: H256) -> FixedSecureTrie<<D as Database<'a>>::Guard, H256, M256> {
        self.database.create_fixed_secure_trie::<H256, M256>(root)
    }

    pub fn storage_state<'a>(&'a self, address: Address) -> Option<FixedSecureTrie<<D as Database<'a>>::Guard, H256, M256>> {
        let state = self.state();
        let account = state.get(&address);

        match account {
            Some(account) => {
                Some(self.storage_state_of(account.storage_root))
            },
            None => None,
        }
    }
}

pub type MemoryStateful = Stateful<MemoryDatabase>;
<commit_msg>stateful: Account::IncreaseBalance might hit an non-existing account<commit_after>#![deny(unused_import_braces,
        unused_comparisons, unused_must_use,
        unused_variables, non_shorthand_field_patterns,
        unreachable_code)]

extern crate trie;
extern crate sputnikvm;
extern crate sha3;
extern crate block;
extern crate rlp;

use sputnikvm::{H256, U256, M256, Address};
use sputnikvm::vm::{self, ValidTransaction, HeaderParams, Memory, TransactionVM, VM,
                    AccountCommitment, Patch, SeqMemory, AccountState};
use sputnikvm::vm::errors::{PreExecutionError, RequireError};
use sha3::{Keccak256, Digest};
use trie::{Trie, SecureTrie, FixedSecureTrie, DatabaseGuard, MemoryDatabase, MemoryDatabaseGuard, Database, DatabaseOwned};
use block::{Account, Transaction};
use std::collections::HashMap;
use std::cmp::min;

pub struct Stateful<D> {
    database: D,
    root: H256,
}

impl<D> Stateful<D> {
    pub fn new(database: D, root: H256) -> Self {
        Self {
            database,
            root
        }
    }
}

impl<D: Default> Default for Stateful<D> {
    fn default() -> Self {
        Self {
            database: D::default(),
            root: MemoryDatabase::new().create_empty().root(),
        }
    }
}

impl<D: DatabaseOwned> Stateful<D> {
    fn is_empty_hash(hash: H256) -> bool {
        hash == H256::from(Keccak256::digest(&[]).as_slice())
    }

    pub fn code(&self, hash: H256) -> Option<Vec<u8>> {
        let code_hashes = self.database.create_guard();

        if Self::is_empty_hash(hash) {
            Some(Vec::new())
        } else {
            code_hashes.get(hash)
        }
    }

    pub fn call<M: Memory + Default>(
        &self, transaction: ValidTransaction, block: HeaderParams,
        patch: &'static Patch, most_recent_block_hashes: &[H256]
    ) -> TransactionVM<M> {
        assert!(U256::from(most_recent_block_hashes.len()) >=
                min(block.number, U256::from(256)));

        let mut vm = TransactionVM::new(transaction, block.clone(), patch);
        let state = self.database.create_fixed_secure_trie(self.root);
        let code_hashes = self.database.create_guard();

        loop {
            match vm.fire() {
                Ok(()) => break,
                Err(RequireError::Account(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            vm.commit_account(AccountCommitment::Full {
                                nonce: account.nonce,
                                address: address,
                                balance: account.balance,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountCode(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            vm.commit_account(AccountCommitment::Code {
                                address: address,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountStorage(address, index)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let storage = self.database.create_fixed_secure_trie(account.storage_root);
                            let value = storage.get(&H256::from(index)).unwrap_or(M256::zero());

                            vm.commit_account(AccountCommitment::Storage {
                                address: address,
                                index, value
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::Blockhash(number)) => {
                    let index = (block.number - number).as_usize();
                    vm.commit_blockhash(number, most_recent_block_hashes[index]).unwrap();
                },
            }
        }

        vm
    }

    pub fn transit(
        &mut self, accounts: &[vm::Account]
    ) {
        let mut state = self.database.create_fixed_secure_trie(self.root);
        let mut code_hashes = self.database.create_guard();

        for account in accounts {
            match account.clone() {
                vm::Account::Full {
                    nonce, address, balance, changing_storage, code
                } => {
                    let changing_storage: HashMap<U256, M256> = changing_storage.into();

                    let mut account: Account = state.get(&address).unwrap();

                    let mut storage_trie = self.database.create_fixed_secure_trie(account.storage_root);
                    for (key, value) in changing_storage {
                        if value == M256::zero() {
                            storage_trie.remove(&H256::from(key));
                        } else {
                            storage_trie.insert(H256::from(key), value);
                        }
                    }

                    account.balance = balance;
                    account.nonce = nonce;
                    account.storage_root = storage_trie.root();
                    assert!(account.code_hash == H256::from(Keccak256::digest(&code).as_slice()));

                    state.insert(address, account);
                },
                vm::Account::IncreaseBalance(address, value) => {
                    match state.get(&address) {
                        Some(mut account) => {
                            account.balance = account.balance + value;
                            state.insert(address, account);
                        },
                        None => {
                            let account = Account {
                                nonce: U256::zero(),
                                balance: value,
                                storage_root: self.database.create_empty().root(),
                                code_hash: H256::from(Keccak256::digest(&[]).as_slice())
                            };
                            state.insert(address, account);
                        }
                    }
                },
                vm::Account::DecreaseBalance(address, value) => {
                    let mut account: Account = state.get(&address).unwrap();

                    account.balance = account.balance - value;

                    state.insert(address, account);
                },
                vm::Account::Create {
                    nonce, address, balance, storage, code, exists
                } => {
                    if !exists {
                        state.remove(&address);
                    } else {
                        let storage: HashMap<U256, M256> = storage.into();

                        let mut storage_trie = self.database.create_fixed_secure_empty();
                        for (key, value) in storage {
                            if value == M256::zero() {
                                storage_trie.remove(&H256::from(key));
                            } else {
                                storage_trie.insert(H256::from(key), value);
                            }
                        }

                        let code_hash = H256::from(Keccak256::digest(&code).as_slice());
                        code_hashes.set(code_hash, code);

                        let account = Account {
                            nonce: nonce,
                            balance: balance,
                            storage_root: storage_trie.root(),
                            code_hash
                        };

                        state.insert(address, account);
                    }
                },
            }
        }

        self.root = state.root();
    }

    pub fn execute<M: Memory + Default>(
        &mut self, transaction: ValidTransaction, block: HeaderParams,
        patch: &'static Patch, most_recent_block_hashes: &[H256]
    ) -> TransactionVM<M> {
        let vm = self.call(transaction, block, patch, most_recent_block_hashes);
        let mut accounts = Vec::new();
        for account in vm.accounts() {
            accounts.push(account.clone());
        }
        self.transit(&accounts);
        vm
    }

    pub fn to_valid(
        &self, transaction: Transaction, patch: &'static Patch
    ) -> Result<ValidTransaction, PreExecutionError> {
        let state = self.database.create_fixed_secure_trie(self.root);
        let code_hashes = self.database.create_guard();
        let mut account_state = AccountState::default();

        loop {
            match ValidTransaction::from_transaction(&transaction, &account_state, patch) {
                Ok(val) => return val,
                Err(RequireError::Account(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            account_state.commit(AccountCommitment::Full {
                                nonce: account.nonce,
                                address: address,
                                balance: account.balance,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            account_state.commit(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountCode(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            account_state.commit(AccountCommitment::Code {
                                address: address,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            account_state.commit(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountStorage(address, index)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let storage = self.database.create_fixed_secure_trie(account.storage_root);
                            let value = storage.get(&H256::from(index)).unwrap_or(M256::zero());

                            account_state.commit(AccountCommitment::Storage {
                                address: address,
                                index, value
                            }).unwrap();
                        },
                        None => {
                            account_state.commit(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::Blockhash(_)) => {
                    panic!()
                },
            }
        }
    }

    pub fn root(&self) -> H256 {
        self.root
    }

    pub fn state_of<'a>(&'a self, root: H256) -> FixedSecureTrie<<D as Database<'a>>::Guard, Address, Account> {
        self.database.create_fixed_secure_trie::<Address, Account>(root)
    }

    pub fn state<'a>(&'a self) -> FixedSecureTrie<<D as Database<'a>>::Guard, Address, Account> {
        self.state_of(self.root())
    }

    pub fn storage_state_of<'a>(&'a self, root: H256) -> FixedSecureTrie<<D as Database<'a>>::Guard, H256, M256> {
        self.database.create_fixed_secure_trie::<H256, M256>(root)
    }

    pub fn storage_state<'a>(&'a self, address: Address) -> Option<FixedSecureTrie<<D as Database<'a>>::Guard, H256, M256>> {
        let state = self.state();
        let account = state.get(&address);

        match account {
            Some(account) => {
                Some(self.storage_state_of(account.storage_root))
            },
            None => None,
        }
    }
}

pub type MemoryStateful = Stateful<MemoryDatabase>;
<|endoftext|>


<commit_before>extern crate gtk;
extern crate glib;
extern crate gstreamer as gst;

#[cfg(any(feature = "profiling-listener", feature = "profiling-tracker"))]
use chrono::Utc;

use std::rc::Rc;
use std::cell::RefCell;

use std::path::PathBuf;

use std::sync::mpsc::{channel, Receiver};

use gtk::prelude::*;

use media::{Context, ContextMessage};
use media::ContextMessage::*;

use super::{AudioController, DoubleWaveformBuffer, InfoController, VideoController};

pub struct MainController {
    window: gtk::ApplicationWindow,
    header_bar: gtk::HeaderBar,
    play_pause_btn: gtk::ToolButton,

    video_ctrl: VideoController,
    info_ctrl: InfoController,
    audio_ctrl: AudioController,

    context: Option<Context>,
    seeking: bool,

    this_opt: Option<Rc<RefCell<MainController>>>,
    keep_going: bool,
    listener_src: Option<glib::SourceId>,
    tracker_src: Option<glib::SourceId>,
}

impl MainController {
    pub fn new(builder: gtk::Builder) -> Rc<RefCell<Self>> {
        let this = Rc::new(RefCell::new(MainController {
            window: builder.get_object("application-window").unwrap(),
            header_bar: builder.get_object("header-bar").unwrap(),
            play_pause_btn: builder.get_object("play_pause-toolbutton").unwrap(),

            video_ctrl: VideoController::new(&builder),
            info_ctrl: InfoController::new(&builder),
            audio_ctrl: AudioController::new(&builder),

            context: None,
            seeking: false,

            this_opt: None,
            keep_going: true,
            listener_src: None,
            tracker_src: None,
        }));

        {
            let mut this_mut = this.borrow_mut();

            let this_rc = Rc::clone(&this);
            this_mut.this_opt = Some(this_rc);

            this_mut.window.connect_delete_event(|_, _| {
                gtk::main_quit();
                Inhibit(false)
            });
            this_mut.window.set_titlebar(&this_mut.header_bar);

            let this_rc = Rc::clone(&this);
            this_mut.play_pause_btn.connect_clicked(move |_| {
                this_rc.borrow_mut().play_pause();
            });

            this_mut.video_ctrl.register_callbacks(&this);
            this_mut.info_ctrl.register_callbacks(&this);
            this_mut.audio_ctrl.register_callbacks(&this);
        }

        let open_btn: gtk::Button = builder.get_object("open-btn").unwrap();
        let this_rc = Rc::clone(&this);
        open_btn.connect_clicked(move |_| {
            this_rc.borrow_mut().select_media();
        });

        this
    }

    pub fn show_all(&self) {
        self.window.show_all();
    }

    fn play_pause(&mut self) {
        let context =
            match self.context.take() {
                Some(context) => context,
                None => {
                    self.select_media();
                    return;
                },
            };

        match context.get_state() {
            gst::State::Paused => {
                self.register_tracker(17); // 60 Hz
                self.play_pause_btn.set_icon_name("media-playback-pause");
                context.play().unwrap();
            }
            gst::State::Playing => {
                context.pause().unwrap();
                self.play_pause_btn.set_icon_name("media-playback-start");
                self.remove_tracker();
            },
            state => println!("Can't play/pause in state {:?}", state),
        };

        self.context = Some(context);
    }

    fn stop(&mut self) {
        if let Some(context) = self.context.as_mut() {
            context.stop();
        };

        // remove callbacks in order to avoid conflict on borrowing of self
        self.remove_listener();
        self.remove_tracker();

        self.audio_ctrl.cleanup();

        self.play_pause_btn.set_icon_name("media-playback-start");
    }

    pub fn seek(&mut self, position: u64) {
        self.seeking = true;
        self.audio_ctrl.seeking();
        self.context.as_ref()
            .expect("No context found while seeking in media")
            .seek(position);
        // update position eventhough the stream
        // is not sync yet for the user to notice
        // the seek request in being handled
        self.info_ctrl.seek(position);
    }

    fn select_media(&mut self) {
        self.stop();

        let file_dlg = gtk::FileChooserDialog::new(
            Some("Open a media file"),
            Some(&self.window),
            gtk::FileChooserAction::Open,
        );
        // Note: couldn't find equivalents for STOCK_OK
        file_dlg.add_button("Open", gtk::ResponseType::Ok.into());

        if file_dlg.run() == gtk::ResponseType::Ok.into() {
            self.open_media(file_dlg.get_filename().unwrap());
        }

        file_dlg.close();
    }

    fn remove_listener(&mut self) {
        if let Some(source_id) = self.listener_src.take() {
            glib::source_remove(source_id);
        }
    }

    fn register_listener(&mut self,
        timeout: u32,
        ui_rx: Receiver<ContextMessage>,
    ) {
        let this_rc = Rc::clone(self.this_opt.as_ref().unwrap());

        self.listener_src = Some(gtk::timeout_add(timeout, move || {
            let mut keep_going = true;

            for message in ui_rx.try_iter() {
                match message {
                    AsyncDone => {
                        this_rc.borrow_mut().seeking = false;
                    },
                    InitDone => {
                        let mut this_mut = this_rc.borrow_mut();

                        let context = this_mut.context.take()
                            .expect("MainController: InitDone but no context available");

                        this_mut.header_bar.set_subtitle(
                            Some(context.file_name.as_str())
                        );

                        this_mut.info_ctrl.new_media(&context);
                        this_mut.video_ctrl.new_media(&context);
                        this_mut.audio_ctrl.new_media(&context);

                        this_mut.context = Some(context);
                    },
                    Eos => {
                        let this = this_rc.borrow_mut();

                        this.audio_ctrl.tic();
                        this.play_pause_btn.set_icon_name("media-playback-start");
                    },
                    FailedToOpenMedia => {
                        eprintln!("ERROR: failed to open media");

                        let mut this_mut = this_rc.borrow_mut();

                        this_mut.context = None;
                        this_mut.keep_going = false;
                        keep_going = false;
                    },
                };

                if !keep_going { break; }
            }

            if !keep_going {
                let mut this_mut = this_rc.borrow_mut();
                this_mut.listener_src = None;
                this_mut.tracker_src = None;
            }

            glib::Continue(keep_going)
        }));
    }

    fn remove_tracker(&mut self) {
        if let Some(source_id) = self.tracker_src.take() {
            glib::source_remove(source_id);
        }
    }

    fn register_tracker(&mut self, timeout: u32) {
        let this_rc = Rc::clone(self.this_opt.as_ref().unwrap());

        self.tracker_src = Some(gtk::timeout_add(timeout, move || {
            #[cfg(feature = "profiling-tracker")]
            let start = Utc::now();

            let mut this_mut = this_rc.borrow_mut();

            #[cfg(feature = "profiling-tracker")]
            let before_tic = Utc::now();

            if !this_mut.seeking {
                let position = this_mut.context.as_mut()
                    .expect("No context in tracker while getting position")
                    .get_position();
                this_mut.info_ctrl.tic(position);
            }

            this_mut.audio_ctrl.tic();

            #[cfg(feature = "profiling-tracker")]
            let end = Utc::now();

            #[cfg(feature = "profiling-tracker")]
            println!("tracker,{},{},{},{}",
                start.time().format("%H:%M:%S%.6f"),
                before_tic.time().format("%H:%M:%S%.6f"),
                end.time().format("%H:%M:%S%.6f"),
                position,
            );

            glib::Continue(this_mut.keep_going)
        }));
    }

    fn open_media(&mut self, filepath: PathBuf) {
        assert_eq!(self.listener_src, None);

        self.info_ctrl.cleanup();
        self.audio_ctrl.cleanup();
        self.header_bar.set_subtitle("");

        let (ctx_tx, ui_rx) = channel();

        self.seeking = false;
        self.keep_going = true;
        self.register_listener(250, ui_rx);

        match Context::new(
            filepath,
            5_000_000_000,
            DoubleWaveformBuffer::new(&self.audio_ctrl.waveform_buffer_mtx),
            self.video_ctrl.video_box.clone(),
            ctx_tx
        ) {
            Ok(context) => {
                self.context = Some(context);
            },
            Err(error) => eprintln!("Error opening media: {}", error),
        };
    }
}
<commit_msg>Fix profiling (part 2)<commit_after>extern crate gtk;
extern crate glib;
extern crate gstreamer as gst;

#[cfg(any(feature = "profiling-listener", feature = "profiling-tracker"))]
use chrono::Utc;

use std::rc::Rc;
use std::cell::RefCell;

use std::path::PathBuf;

use std::sync::mpsc::{channel, Receiver};

use gtk::prelude::*;

use media::{Context, ContextMessage};
use media::ContextMessage::*;

use super::{AudioController, DoubleWaveformBuffer, InfoController, VideoController};

pub struct MainController {
    window: gtk::ApplicationWindow,
    header_bar: gtk::HeaderBar,
    play_pause_btn: gtk::ToolButton,

    video_ctrl: VideoController,
    info_ctrl: InfoController,
    audio_ctrl: AudioController,

    context: Option<Context>,
    seeking: bool,

    this_opt: Option<Rc<RefCell<MainController>>>,
    keep_going: bool,
    listener_src: Option<glib::SourceId>,
    tracker_src: Option<glib::SourceId>,
}

impl MainController {
    pub fn new(builder: gtk::Builder) -> Rc<RefCell<Self>> {
        let this = Rc::new(RefCell::new(MainController {
            window: builder.get_object("application-window").unwrap(),
            header_bar: builder.get_object("header-bar").unwrap(),
            play_pause_btn: builder.get_object("play_pause-toolbutton").unwrap(),

            video_ctrl: VideoController::new(&builder),
            info_ctrl: InfoController::new(&builder),
            audio_ctrl: AudioController::new(&builder),

            context: None,
            seeking: false,

            this_opt: None,
            keep_going: true,
            listener_src: None,
            tracker_src: None,
        }));

        {
            let mut this_mut = this.borrow_mut();

            let this_rc = Rc::clone(&this);
            this_mut.this_opt = Some(this_rc);

            this_mut.window.connect_delete_event(|_, _| {
                gtk::main_quit();
                Inhibit(false)
            });
            this_mut.window.set_titlebar(&this_mut.header_bar);

            let this_rc = Rc::clone(&this);
            this_mut.play_pause_btn.connect_clicked(move |_| {
                this_rc.borrow_mut().play_pause();
            });

            this_mut.video_ctrl.register_callbacks(&this);
            this_mut.info_ctrl.register_callbacks(&this);
            this_mut.audio_ctrl.register_callbacks(&this);
        }

        let open_btn: gtk::Button = builder.get_object("open-btn").unwrap();
        let this_rc = Rc::clone(&this);
        open_btn.connect_clicked(move |_| {
            this_rc.borrow_mut().select_media();
        });

        this
    }

    pub fn show_all(&self) {
        self.window.show_all();
    }

    fn play_pause(&mut self) {
        let context =
            match self.context.take() {
                Some(context) => context,
                None => {
                    self.select_media();
                    return;
                },
            };

        match context.get_state() {
            gst::State::Paused => {
                self.register_tracker(17); // 60 Hz
                self.play_pause_btn.set_icon_name("media-playback-pause");
                context.play().unwrap();
            }
            gst::State::Playing => {
                context.pause().unwrap();
                self.play_pause_btn.set_icon_name("media-playback-start");
                self.remove_tracker();
            },
            state => println!("Can't play/pause in state {:?}", state),
        };

        self.context = Some(context);
    }

    fn stop(&mut self) {
        if let Some(context) = self.context.as_mut() {
            context.stop();
        };

        // remove callbacks in order to avoid conflict on borrowing of self
        self.remove_listener();
        self.remove_tracker();

        self.audio_ctrl.cleanup();

        self.play_pause_btn.set_icon_name("media-playback-start");
    }

    pub fn seek(&mut self, position: u64) {
        self.seeking = true;
        self.audio_ctrl.seeking();
        self.context.as_ref()
            .expect("No context found while seeking in media")
            .seek(position);
        // update position eventhough the stream
        // is not sync yet for the user to notice
        // the seek request in being handled
        self.info_ctrl.seek(position);
    }

    fn select_media(&mut self) {
        self.stop();

        let file_dlg = gtk::FileChooserDialog::new(
            Some("Open a media file"),
            Some(&self.window),
            gtk::FileChooserAction::Open,
        );
        // Note: couldn't find equivalents for STOCK_OK
        file_dlg.add_button("Open", gtk::ResponseType::Ok.into());

        if file_dlg.run() == gtk::ResponseType::Ok.into() {
            self.open_media(file_dlg.get_filename().unwrap());
        }

        file_dlg.close();
    }

    fn remove_listener(&mut self) {
        if let Some(source_id) = self.listener_src.take() {
            glib::source_remove(source_id);
        }
    }

    fn register_listener(&mut self,
        timeout: u32,
        ui_rx: Receiver<ContextMessage>,
    ) {
        let this_rc = Rc::clone(self.this_opt.as_ref().unwrap());

        self.listener_src = Some(gtk::timeout_add(timeout, move || {
            let mut keep_going = true;

            for message in ui_rx.try_iter() {
                match message {
                    AsyncDone => {
                        this_rc.borrow_mut().seeking = false;
                    },
                    InitDone => {
                        let mut this_mut = this_rc.borrow_mut();

                        let context = this_mut.context.take()
                            .expect("MainController: InitDone but no context available");

                        this_mut.header_bar.set_subtitle(
                            Some(context.file_name.as_str())
                        );

                        this_mut.info_ctrl.new_media(&context);
                        this_mut.video_ctrl.new_media(&context);
                        this_mut.audio_ctrl.new_media(&context);

                        this_mut.context = Some(context);
                    },
                    Eos => {
                        let this = this_rc.borrow_mut();

                        this.audio_ctrl.tic();
                        this.play_pause_btn.set_icon_name("media-playback-start");
                    },
                    FailedToOpenMedia => {
                        eprintln!("ERROR: failed to open media");

                        let mut this_mut = this_rc.borrow_mut();

                        this_mut.context = None;
                        this_mut.keep_going = false;
                        keep_going = false;
                    },
                };

                if !keep_going { break; }
            }

            if !keep_going {
                let mut this_mut = this_rc.borrow_mut();
                this_mut.listener_src = None;
                this_mut.tracker_src = None;
            }

            glib::Continue(keep_going)
        }));
    }

    fn remove_tracker(&mut self) {
        if let Some(source_id) = self.tracker_src.take() {
            glib::source_remove(source_id);
        }
    }

    fn register_tracker(&mut self, timeout: u32) {
        let this_rc = Rc::clone(self.this_opt.as_ref().unwrap());

        self.tracker_src = Some(gtk::timeout_add(timeout, move || {
            #[cfg(feature = "profiling-tracker")]
            let start = Utc::now();

            let mut this_mut = this_rc.borrow_mut();

            #[cfg(feature = "profiling-tracker")]
            let before_tic = Utc::now();

            if !this_mut.seeking {
                let position = this_mut.context.as_mut()
                    .expect("No context in tracker while getting position")
                    .get_position();
                this_mut.info_ctrl.tic(position);
            }

            this_mut.audio_ctrl.tic();

            #[cfg(feature = "profiling-tracker")]
            let end = Utc::now();

            #[cfg(feature = "profiling-tracker")]
            println!("tracker,{},{},{}",
                start.time().format("%H:%M:%S%.6f"),
                before_tic.time().format("%H:%M:%S%.6f"),
                end.time().format("%H:%M:%S%.6f"),
            );

            glib::Continue(this_mut.keep_going)
        }));
    }

    fn open_media(&mut self, filepath: PathBuf) {
        assert_eq!(self.listener_src, None);

        self.info_ctrl.cleanup();
        self.audio_ctrl.cleanup();
        self.header_bar.set_subtitle("");

        let (ctx_tx, ui_rx) = channel();

        self.seeking = false;
        self.keep_going = true;
        self.register_listener(250, ui_rx);

        match Context::new(
            filepath,
            5_000_000_000,
            DoubleWaveformBuffer::new(&self.audio_ctrl.waveform_buffer_mtx),
            self.video_ctrl.video_box.clone(),
            ctx_tx
        ) {
            Ok(context) => {
                self.context = Some(context);
            },
            Err(error) => eprintln!("Error opening media: {}", error),
        };
    }
}
<|endoftext|>


<commit_before>use rustc_index::vec::{Idx, IndexVec};
use std::mem;
use std::ptr;

pub trait IdFunctor {
    type Inner;

    fn map_id<F>(self, f: F) -> Self
    where
        F: FnMut(Self::Inner) -> Self::Inner;
}

impl<T> IdFunctor for Box<T> {
    type Inner = T;

    #[inline]
    fn map_id<F>(self, mut f: F) -> Self
    where
        F: FnMut(Self::Inner) -> Self::Inner,
    {
        let raw = Box::into_raw(self);
        unsafe {
            // SAFETY: The raw pointer points to a valid value of type `T`.
            let value = ptr::read(raw);
            // SAFETY: Converts `Box<T>` to `Box<MaybeUninit<T>>` which is the
            // inverse of `Box::assume_init()` and should be safe.
            let mut raw: Box<mem::MaybeUninit<T>> = Box::from_raw(raw.cast());
            // SAFETY: Write the mapped value back into the `Box`.
            ptr::write(raw.as_mut_ptr(), f(value));
            // SAFETY: We just initialized `raw`.
            raw.assume_init()
        }
    }
}

impl<T> IdFunctor for Vec<T> {
    type Inner = T;

    #[inline]
    fn map_id<F>(mut self, mut f: F) -> Self
    where
        F: FnMut(Self::Inner) -> Self::Inner,
    {
        // FIXME: We don't really care about panics here and leak
        // far more than we should, but that should be fine for now.
        let len = self.len();
        unsafe {
            self.set_len(0);
            let start = self.as_mut_ptr();
            for i in 0..len {
                let p = start.add(i);
                ptr::write(p, f(ptr::read(p)));
            }
            self.set_len(len);
        }
        self
    }
}

impl<T> IdFunctor for Box<[T]> {
    type Inner = T;

    #[inline]
    fn map_id<F>(self, f: F) -> Self
    where
        F: FnMut(Self::Inner) -> Self::Inner,
    {
        Vec::from(self).map_id(f).into()
    }
}

impl<I: Idx, T> IdFunctor for IndexVec<I, T> {
    type Inner = T;

    #[inline]
    fn map_id<F>(self, f: F) -> Self
    where
        F: FnMut(Self::Inner) -> Self::Inner,
    {
        IndexVec::from_raw(self.raw.map_id(f))
    }
}
<commit_msg>Rollup merge of #88492 - est31:maybe_uninit_write, r=wesleywiser<commit_after>use rustc_index::vec::{Idx, IndexVec};
use std::mem;
use std::ptr;

pub trait IdFunctor {
    type Inner;

    fn map_id<F>(self, f: F) -> Self
    where
        F: FnMut(Self::Inner) -> Self::Inner;
}

impl<T> IdFunctor for Box<T> {
    type Inner = T;

    #[inline]
    fn map_id<F>(self, mut f: F) -> Self
    where
        F: FnMut(Self::Inner) -> Self::Inner,
    {
        let raw = Box::into_raw(self);
        unsafe {
            // SAFETY: The raw pointer points to a valid value of type `T`.
            let value = ptr::read(raw);
            // SAFETY: Converts `Box<T>` to `Box<MaybeUninit<T>>` which is the
            // inverse of `Box::assume_init()` and should be safe.
            let mut raw: Box<mem::MaybeUninit<T>> = Box::from_raw(raw.cast());
            // SAFETY: Write the mapped value back into the `Box`.
            raw.write(f(value));
            // SAFETY: We just initialized `raw`.
            raw.assume_init()
        }
    }
}

impl<T> IdFunctor for Vec<T> {
    type Inner = T;

    #[inline]
    fn map_id<F>(mut self, mut f: F) -> Self
    where
        F: FnMut(Self::Inner) -> Self::Inner,
    {
        // FIXME: We don't really care about panics here and leak
        // far more than we should, but that should be fine for now.
        let len = self.len();
        unsafe {
            self.set_len(0);
            let start = self.as_mut_ptr();
            for i in 0..len {
                let p = start.add(i);
                ptr::write(p, f(ptr::read(p)));
            }
            self.set_len(len);
        }
        self
    }
}

impl<T> IdFunctor for Box<[T]> {
    type Inner = T;

    #[inline]
    fn map_id<F>(self, f: F) -> Self
    where
        F: FnMut(Self::Inner) -> Self::Inner,
    {
        Vec::from(self).map_id(f).into()
    }
}

impl<I: Idx, T> IdFunctor for IndexVec<I, T> {
    type Inner = T;

    #[inline]
    fn map_id<F>(self, f: F) -> Self
    where
        F: FnMut(Self::Inner) -> Self::Inner,
    {
        IndexVec::from_raw(self.raw.map_id(f))
    }
}
<|endoftext|>


<commit_before>use super::utils::{LspPositionConversion, TupleToPosition};
use crate::{
    compiler::{
        cst::{Cst, CstKind, UnwrapWhitespaceAndComment},
        rcst_to_cst::RcstToCst,
    },
    input::Input,
};
use lsp_types::{FoldingRange, FoldingRangeKind};

#[salsa::query_group(FoldingRangeDbStorage)]
pub trait FoldingRangeDb: LspPositionConversion + RcstToCst {
    fn folding_ranges(&self, input: Input) -> Vec<FoldingRange>;
}

fn folding_ranges(db: &dyn FoldingRangeDb, input: Input) -> Vec<FoldingRange> {
    let mut context = Context::new(db, input.clone());
    let cst = db.cst(input).unwrap();
    context.visit_csts(&cst);
    context.ranges
}

struct Context<'a> {
    db: &'a dyn FoldingRangeDb,
    input: Input,
    ranges: Vec<FoldingRange>,
}
impl<'a> Context<'a> {
    fn new(db: &'a dyn FoldingRangeDb, input: Input) -> Self {
        Context {
            db,
            input,
            ranges: vec![],
        }
    }

    fn visit_csts(&mut self, csts: &[Cst]) {
        for cst in csts {
            self.visit_cst(cst);
        }
    }
    fn visit_cst(&mut self, cst: &Cst) {
        match &cst.kind {
            CstKind::EqualsSign => {}
            CstKind::Comma => {}
            CstKind::Dot => {}
            CstKind::Colon => {}
            CstKind::ColonEqualsSign => {}
            CstKind::OpeningParenthesis => {}
            CstKind::ClosingParenthesis => {}
            CstKind::OpeningBracket => {}
            CstKind::ClosingBracket => {}
            CstKind::OpeningCurlyBrace => {}
            CstKind::ClosingCurlyBrace => {}
            CstKind::Arrow => {}
            CstKind::DoubleQuote => {}
            CstKind::Octothorpe => {}
            CstKind::Whitespace(_) => {}
            CstKind::Newline(_) => {}
            // TODO: support folding ranges for comments
            CstKind::Comment { .. } => {}
            CstKind::TrailingWhitespace { child, .. } => self.visit_cst(child),
            CstKind::Identifier(_) => {}
            CstKind::Symbol(_) => {}
            CstKind::Int { .. } => {}
            // TODO: support folding ranges for multiline texts
            CstKind::Text { .. } => {}
            CstKind::TextPart(_) => {}
            CstKind::Parenthesized { inner, .. } => self.visit_cst(inner),
            CstKind::Call { name, arguments } => {
                if !arguments.is_empty() {
                    let name = name.unwrap_whitespace_and_comment();
                    let last_argument = arguments.last().unwrap().unwrap_whitespace_and_comment();
                    self.push(
                        name.span.end,
                        last_argument.span.end,
                        FoldingRangeKind::Region,
                    );
                }

                self.visit_cst(name);
                self.visit_csts(&arguments);
            }
            // TODO: support folding ranges for structs
            CstKind::Struct { fields, .. } => self.visit_csts(fields),
            CstKind::StructField { key, value, .. } => {
                self.visit_cst(key);
                self.visit_cst(value);
            }
            CstKind::StructAccess { struct_, dot, key } => {
                self.visit_cst(struct_);
                self.visit_cst(dot);
                self.visit_cst(key);
            }
            CstKind::Lambda {
                opening_curly_brace,
                parameters_and_arrow,
                body,
                closing_curly_brace,
            } => {
                let opening_curly_brace = opening_curly_brace.unwrap_whitespace_and_comment();
                assert!(matches!(
                    opening_curly_brace.kind,
                    CstKind::OpeningCurlyBrace { .. }
                ));

                let closing_curly_brace = closing_curly_brace.unwrap_whitespace_and_comment();
                assert!(matches!(
                    closing_curly_brace.kind,
                    CstKind::ClosingCurlyBrace { .. }
                ));

                self.push(
                    opening_curly_brace.span.end,
                    closing_curly_brace.span.start,
                    FoldingRangeKind::Region,
                );
                if let Some((parameters, _)) = parameters_and_arrow {
                    self.visit_csts(&parameters);
                }
                self.visit_csts(&body);
            }
            CstKind::Assignment {
                name,
                assignment_sign,
                parameters,
                body,
            } => {
                if !body.is_empty() {
                    let assignment_sign = assignment_sign.unwrap_whitespace_and_comment();
                    let last_expression = body.last().unwrap().unwrap_whitespace_and_comment();

                    self.push(
                        assignment_sign.span.end,
                        last_expression.span.end,
                        FoldingRangeKind::Region,
                    );
                }

                self.visit_cst(name);
                self.visit_csts(&parameters);
                self.visit_csts(&body);
            }
            CstKind::Error { .. } => {}
        }
    }

    fn push(&mut self, start: usize, end: usize, kind: FoldingRangeKind) {
        let start = self
            .db
            .offset_to_lsp(self.input.clone(), start)
            .to_position();
        let end = self.db.offset_to_lsp(self.input.clone(), end).to_position();

        self.ranges.push(FoldingRange {
            start_line: start.line,
            start_character: Some(start.character),
            end_line: end.line,
            end_character: Some(end.character),
            kind: Some(kind),
        });
    }
}
<commit_msg>Remove invalid assert in folding_range<commit_after>use super::utils::{LspPositionConversion, TupleToPosition};
use crate::{
    compiler::{
        cst::{Cst, CstKind, UnwrapWhitespaceAndComment},
        rcst_to_cst::RcstToCst,
    },
    input::Input,
};
use lsp_types::{FoldingRange, FoldingRangeKind};

#[salsa::query_group(FoldingRangeDbStorage)]
pub trait FoldingRangeDb: LspPositionConversion + RcstToCst {
    fn folding_ranges(&self, input: Input) -> Vec<FoldingRange>;
}

fn folding_ranges(db: &dyn FoldingRangeDb, input: Input) -> Vec<FoldingRange> {
    let mut context = Context::new(db, input.clone());
    let cst = db.cst(input).unwrap();
    context.visit_csts(&cst);
    context.ranges
}

struct Context<'a> {
    db: &'a dyn FoldingRangeDb,
    input: Input,
    ranges: Vec<FoldingRange>,
}
impl<'a> Context<'a> {
    fn new(db: &'a dyn FoldingRangeDb, input: Input) -> Self {
        Context {
            db,
            input,
            ranges: vec![],
        }
    }

    fn visit_csts(&mut self, csts: &[Cst]) {
        for cst in csts {
            self.visit_cst(cst);
        }
    }
    fn visit_cst(&mut self, cst: &Cst) {
        match &cst.kind {
            CstKind::EqualsSign => {}
            CstKind::Comma => {}
            CstKind::Dot => {}
            CstKind::Colon => {}
            CstKind::ColonEqualsSign => {}
            CstKind::OpeningParenthesis => {}
            CstKind::ClosingParenthesis => {}
            CstKind::OpeningBracket => {}
            CstKind::ClosingBracket => {}
            CstKind::OpeningCurlyBrace => {}
            CstKind::ClosingCurlyBrace => {}
            CstKind::Arrow => {}
            CstKind::DoubleQuote => {}
            CstKind::Octothorpe => {}
            CstKind::Whitespace(_) => {}
            CstKind::Newline(_) => {}
            // TODO: support folding ranges for comments
            CstKind::Comment { .. } => {}
            CstKind::TrailingWhitespace { child, .. } => self.visit_cst(child),
            CstKind::Identifier(_) => {}
            CstKind::Symbol(_) => {}
            CstKind::Int { .. } => {}
            // TODO: support folding ranges for multiline texts
            CstKind::Text { .. } => {}
            CstKind::TextPart(_) => {}
            CstKind::Parenthesized { inner, .. } => self.visit_cst(inner),
            CstKind::Call { name, arguments } => {
                if !arguments.is_empty() {
                    let name = name.unwrap_whitespace_and_comment();
                    let last_argument = arguments.last().unwrap().unwrap_whitespace_and_comment();
                    self.push(
                        name.span.end,
                        last_argument.span.end,
                        FoldingRangeKind::Region,
                    );
                }

                self.visit_cst(name);
                self.visit_csts(&arguments);
            }
            // TODO: support folding ranges for structs
            CstKind::Struct { fields, .. } => self.visit_csts(fields),
            CstKind::StructField { key, value, .. } => {
                self.visit_cst(key);
                self.visit_cst(value);
            }
            CstKind::StructAccess { struct_, dot, key } => {
                self.visit_cst(struct_);
                self.visit_cst(dot);
                self.visit_cst(key);
            }
            CstKind::Lambda {
                opening_curly_brace,
                parameters_and_arrow,
                body,
                closing_curly_brace,
            } => {
                let opening_curly_brace = opening_curly_brace.unwrap_whitespace_and_comment();
                assert!(matches!(
                    opening_curly_brace.kind,
                    CstKind::OpeningCurlyBrace { .. }
                ));

                let closing_curly_brace = closing_curly_brace.unwrap_whitespace_and_comment();

                self.push(
                    opening_curly_brace.span.end,
                    closing_curly_brace.span.start,
                    FoldingRangeKind::Region,
                );
                if let Some((parameters, _)) = parameters_and_arrow {
                    self.visit_csts(&parameters);
                }
                self.visit_csts(&body);
            }
            CstKind::Assignment {
                name,
                assignment_sign,
                parameters,
                body,
            } => {
                if !body.is_empty() {
                    let assignment_sign = assignment_sign.unwrap_whitespace_and_comment();
                    let last_expression = body.last().unwrap().unwrap_whitespace_and_comment();

                    self.push(
                        assignment_sign.span.end,
                        last_expression.span.end,
                        FoldingRangeKind::Region,
                    );
                }

                self.visit_cst(name);
                self.visit_csts(&parameters);
                self.visit_csts(&body);
            }
            CstKind::Error { .. } => {}
        }
    }

    fn push(&mut self, start: usize, end: usize, kind: FoldingRangeKind) {
        let start = self
            .db
            .offset_to_lsp(self.input.clone(), start)
            .to_position();
        let end = self.db.offset_to_lsp(self.input.clone(), end).to_position();

        self.ranges.push(FoldingRange {
            start_line: start.line,
            start_character: Some(start.character),
            end_line: end.line,
            end_character: Some(end.character),
            kind: Some(kind),
        });
    }
}
<|endoftext|>


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

use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::trace::JSTraceable;
use crate::dom::document::Document;
use crate::dom::htmlimageelement::image_fetch_request;
use crate::dom::htmlscriptelement::script_fetch_request;
use crate::stylesheet_loader::stylesheet_fetch_request;
use html5ever::buffer_queue::BufferQueue;
use html5ever::tokenizer::states::RawKind;
use html5ever::tokenizer::Tag;
use html5ever::tokenizer::TagKind;
use html5ever::tokenizer::Token;
use html5ever::tokenizer::TokenSink;
use html5ever::tokenizer::TokenSinkResult;
use html5ever::tokenizer::Tokenizer as HtmlTokenizer;
use html5ever::tokenizer::TokenizerResult;
use html5ever::Attribute;
use html5ever::LocalName;
use js::jsapi::JSTracer;
use msg::constellation_msg::PipelineId;
use net_traits::request::CorsSettings;
use net_traits::request::Referrer;
use net_traits::CoreResourceMsg;
use net_traits::FetchChannels;
use net_traits::IpcSend;
use net_traits::ReferrerPolicy;
use net_traits::ResourceThreads;
use servo_url::ImmutableOrigin;
use servo_url::ServoUrl;

#[derive(JSTraceable, MallocSizeOf)]
#[must_root]
pub struct Tokenizer {
    #[ignore_malloc_size_of = "Defined in html5ever"]
    inner: HtmlTokenizer<PrefetchSink>,
}

#[allow(unsafe_code)]
unsafe impl JSTraceable for HtmlTokenizer<PrefetchSink> {
    unsafe fn trace(&self, trc: *mut JSTracer) {
        self.sink.trace(trc)
    }
}

impl Tokenizer {
    pub fn new(document: &Document) -> Self {
        let sink = PrefetchSink {
            origin: document.origin().immutable().clone(),
            pipeline_id: document.global().pipeline_id(),
            base: document.url(),
            referrer: Referrer::ReferrerUrl(document.url()),
            referrer_policy: document.get_referrer_policy(),
            resource_threads: document.loader().resource_threads().clone(),
            // Initially we set prefetching to false, and only set it
            // true after the first script tag, since that is what will
            // block the main parser.
            prefetching: false,
        };
        let options = Default::default();
        let inner = HtmlTokenizer::new(sink, options);
        Tokenizer { inner }
    }

    pub fn feed(&mut self, input: &mut BufferQueue) {
        while let TokenizerResult::Script(PrefetchHandle) = self.inner.feed(input) {}
    }
}

#[derive(JSTraceable)]
struct PrefetchSink {
    origin: ImmutableOrigin,
    pipeline_id: PipelineId,
    base: ServoUrl,
    referrer: Referrer,
    referrer_policy: Option<ReferrerPolicy>,
    resource_threads: ResourceThreads,
    prefetching: bool,
}

/// The prefetch tokenizer produces trivial results
struct PrefetchHandle;

impl TokenSink for PrefetchSink {
    type Handle = PrefetchHandle;
    fn process_token(
        &mut self,
        token: Token,
        _line_number: u64,
    ) -> TokenSinkResult<PrefetchHandle> {
        let tag = match token {
            Token::TagToken(ref tag) => tag,
            _ => return TokenSinkResult::Continue,
        };
        match (tag.kind, &tag.name) {
            (TagKind::StartTag, local_name!("script")) if self.prefetching => {
                if let Some(url) = self.get_url(tag, local_name!("src")) {
                    debug!("Prefetch script {}", url);
                    let cors_setting = self.get_cors_settings(tag, local_name!("crossorigin"));
                    let integrity_metadata = self
                        .get_attr(tag, local_name!("integrity"))
                        .map(|attr| String::from(&attr.value))
                        .unwrap_or_default();
                    let request = script_fetch_request(
                        url,
                        cors_setting,
                        self.origin.clone(),
                        self.pipeline_id,
                        self.referrer.clone(),
                        self.referrer_policy,
                        integrity_metadata,
                    );
                    let _ = self
                        .resource_threads
                        .send(CoreResourceMsg::Fetch(request, FetchChannels::Prefetch));
                }
                TokenSinkResult::RawData(RawKind::ScriptData)
            },
            (TagKind::StartTag, local_name!("img")) if self.prefetching => {
                if let Some(url) = self.get_url(tag, local_name!("src")) {
                    debug!("Prefetch {} {}", tag.name, url);
                    let request = image_fetch_request(url, self.origin.clone(), self.pipeline_id);
                    let _ = self
                        .resource_threads
                        .send(CoreResourceMsg::Fetch(request, FetchChannels::Prefetch));
                }
                TokenSinkResult::Continue
            },
            (TagKind::StartTag, local_name!("link")) if self.prefetching => {
                if let Some(rel) = self.get_attr(tag, local_name!("rel")) {
                    if rel.value.eq_ignore_ascii_case("stylesheet") {
                        if let Some(url) = self.get_url(tag, local_name!("href")) {
                            debug!("Prefetch {} {}", tag.name, url);
                            let cors_setting =
                                self.get_cors_settings(tag, local_name!("crossorigin"));
                            let integrity_metadata = self
                                .get_attr(tag, local_name!("integrity"))
                                .map(|attr| String::from(&attr.value))
                                .unwrap_or_default();
                            let request = stylesheet_fetch_request(
                                url,
                                cors_setting,
                                self.origin.clone(),
                                self.pipeline_id,
                                self.referrer.clone(),
                                self.referrer_policy,
                                integrity_metadata,
                            );
                            let _ = self
                                .resource_threads
                                .send(CoreResourceMsg::Fetch(request, FetchChannels::Prefetch));
                        }
                    }
                }
                TokenSinkResult::Continue
            },
            (TagKind::StartTag, local_name!("script")) => {
                TokenSinkResult::RawData(RawKind::ScriptData)
            },
            (TagKind::EndTag, local_name!("script")) => {
                // After the first script tag, the main parser is blocked, so it's worth prefetching.
                self.prefetching = true;
                TokenSinkResult::Script(PrefetchHandle)
            },
            (TagKind::StartTag, local_name!("base")) => {
                if let Some(url) = self.get_url(tag, local_name!("href")) {
                    debug!("Setting base {}", url);
                    self.base = url;
                }
                TokenSinkResult::Continue
            },
            _ => TokenSinkResult::Continue,
        }
    }
}

impl PrefetchSink {
    fn get_attr<'a>(&'a self, tag: &'a Tag, name: LocalName) -> Option<&'a Attribute> {
        tag.attrs.iter().find(|attr| attr.name.local == name)
    }

    fn get_url(&self, tag: &Tag, name: LocalName) -> Option<ServoUrl> {
        self.get_attr(tag, name)
            .and_then(|attr| ServoUrl::parse_with_base(Some(&self.base), &attr.value).ok())
    }

    fn get_cors_settings(&self, tag: &Tag, name: LocalName) -> Option<CorsSettings> {
        let crossorigin = self.get_attr(tag, name)?;
        if crossorigin.value.eq_ignore_ascii_case("anonymous") {
            Some(CorsSettings::Anonymous)
        } else if crossorigin.value.eq_ignore_ascii_case("use-credentials") {
            Some(CorsSettings::UseCredentials)
        } else {
            None
        }
    }
}
<commit_msg>Responding to review comments<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::trace::JSTraceable;
use crate::dom::document::Document;
use crate::dom::htmlimageelement::image_fetch_request;
use crate::dom::htmlscriptelement::script_fetch_request;
use crate::stylesheet_loader::stylesheet_fetch_request;
use html5ever::buffer_queue::BufferQueue;
use html5ever::tokenizer::states::RawKind;
use html5ever::tokenizer::Tag;
use html5ever::tokenizer::TagKind;
use html5ever::tokenizer::Token;
use html5ever::tokenizer::TokenSink;
use html5ever::tokenizer::TokenSinkResult;
use html5ever::tokenizer::Tokenizer as HtmlTokenizer;
use html5ever::tokenizer::TokenizerResult;
use html5ever::Attribute;
use html5ever::LocalName;
use js::jsapi::JSTracer;
use msg::constellation_msg::PipelineId;
use net_traits::request::CorsSettings;
use net_traits::request::Referrer;
use net_traits::CoreResourceMsg;
use net_traits::FetchChannels;
use net_traits::IpcSend;
use net_traits::ReferrerPolicy;
use net_traits::ResourceThreads;
use servo_url::ImmutableOrigin;
use servo_url::ServoUrl;

#[derive(JSTraceable, MallocSizeOf)]
#[must_root]
pub struct Tokenizer {
    #[ignore_malloc_size_of = "Defined in html5ever"]
    inner: HtmlTokenizer<PrefetchSink>,
}

#[allow(unsafe_code)]
unsafe impl JSTraceable for HtmlTokenizer<PrefetchSink> {
    unsafe fn trace(&self, trc: *mut JSTracer) {
        self.sink.trace(trc)
    }
}

impl Tokenizer {
    pub fn new(document: &Document) -> Self {
        let sink = PrefetchSink {
            origin: document.origin().immutable().clone(),
            pipeline_id: document.global().pipeline_id(),
            base_url: None,
            document_url: document.url(),
            referrer: Referrer::ReferrerUrl(document.url()),
            referrer_policy: document.get_referrer_policy(),
            resource_threads: document.loader().resource_threads().clone(),
            // Initially we set prefetching to false, and only set it
            // true after the first script tag, since that is what will
            // block the main parser.
            prefetching: false,
        };
        let options = Default::default();
        let inner = HtmlTokenizer::new(sink, options);
        Tokenizer { inner }
    }

    pub fn feed(&mut self, input: &mut BufferQueue) {
        while let TokenizerResult::Script(PrefetchHandle) = self.inner.feed(input) {}
    }
}

#[derive(JSTraceable)]
struct PrefetchSink {
    origin: ImmutableOrigin,
    pipeline_id: PipelineId,
    document_url: ServoUrl,
    base_url: Option<ServoUrl>,
    referrer: Referrer,
    referrer_policy: Option<ReferrerPolicy>,
    resource_threads: ResourceThreads,
    prefetching: bool,
}

/// The prefetch tokenizer produces trivial results
struct PrefetchHandle;

impl TokenSink for PrefetchSink {
    type Handle = PrefetchHandle;
    fn process_token(
        &mut self,
        token: Token,
        _line_number: u64,
    ) -> TokenSinkResult<PrefetchHandle> {
        let tag = match token {
            Token::TagToken(ref tag) => tag,
            _ => return TokenSinkResult::Continue,
        };
        match (tag.kind, &tag.name) {
            (TagKind::StartTag, local_name!("script")) if self.prefetching => {
                if let Some(url) = self.get_url(tag, local_name!("src")) {
                    debug!("Prefetch script {}", url);
                    let cors_setting = self.get_cors_settings(tag, local_name!("crossorigin"));
                    let integrity_metadata = self
                        .get_attr(tag, local_name!("integrity"))
                        .map(|attr| String::from(&attr.value))
                        .unwrap_or_default();
                    let request = script_fetch_request(
                        url,
                        cors_setting,
                        self.origin.clone(),
                        self.pipeline_id,
                        self.referrer.clone(),
                        self.referrer_policy,
                        integrity_metadata,
                    );
                    let _ = self
                        .resource_threads
                        .send(CoreResourceMsg::Fetch(request, FetchChannels::Prefetch));
                }
                TokenSinkResult::RawData(RawKind::ScriptData)
            },
            (TagKind::StartTag, local_name!("img")) if self.prefetching => {
                if let Some(url) = self.get_url(tag, local_name!("src")) {
                    debug!("Prefetch {} {}", tag.name, url);
                    let request = image_fetch_request(url, self.origin.clone(), self.pipeline_id);
                    let _ = self
                        .resource_threads
                        .send(CoreResourceMsg::Fetch(request, FetchChannels::Prefetch));
                }
                TokenSinkResult::Continue
            },
            (TagKind::StartTag, local_name!("link")) if self.prefetching => {
                if let Some(rel) = self.get_attr(tag, local_name!("rel")) {
                    if rel.value.eq_ignore_ascii_case("stylesheet") {
                        if let Some(url) = self.get_url(tag, local_name!("href")) {
                            debug!("Prefetch {} {}", tag.name, url);
                            let cors_setting =
                                self.get_cors_settings(tag, local_name!("crossorigin"));
                            let integrity_metadata = self
                                .get_attr(tag, local_name!("integrity"))
                                .map(|attr| String::from(&attr.value))
                                .unwrap_or_default();
                            let request = stylesheet_fetch_request(
                                url,
                                cors_setting,
                                self.origin.clone(),
                                self.pipeline_id,
                                self.referrer.clone(),
                                self.referrer_policy,
                                integrity_metadata,
                            );
                            let _ = self
                                .resource_threads
                                .send(CoreResourceMsg::Fetch(request, FetchChannels::Prefetch));
                        }
                    }
                }
                TokenSinkResult::Continue
            },
            (TagKind::StartTag, local_name!("script")) => {
                TokenSinkResult::RawData(RawKind::ScriptData)
            },
            (TagKind::EndTag, local_name!("script")) => {
                // After the first script tag, the main parser is blocked, so it's worth prefetching.
                self.prefetching = true;
                TokenSinkResult::Script(PrefetchHandle)
            },
            (TagKind::StartTag, local_name!("base")) => {
                if let Some(url) = self.get_url(tag, local_name!("href")) {
                    if self.base_url.is_none() {
                        debug!("Setting base {}", url);
                        self.base_url = Some(url);
                    }
                }
                TokenSinkResult::Continue
            },
            _ => TokenSinkResult::Continue,
        }
    }
}

impl PrefetchSink {
    fn get_attr<'a>(&'a self, tag: &'a Tag, name: LocalName) -> Option<&'a Attribute> {
        tag.attrs.iter().find(|attr| attr.name.local == name)
    }

    fn get_url(&self, tag: &Tag, name: LocalName) -> Option<ServoUrl> {
        let attr = self.get_attr(tag, name)?;
        let base = self.base_url.as_ref().unwrap_or(&self.document_url);
        ServoUrl::parse_with_base(Some(base), &attr.value).ok()
    }

    fn get_cors_settings(&self, tag: &Tag, name: LocalName) -> Option<CorsSettings> {
        let crossorigin = self.get_attr(tag, name)?;
        if crossorigin.value.eq_ignore_ascii_case("anonymous") {
            Some(CorsSettings::Anonymous)
        } else if crossorigin.value.eq_ignore_ascii_case("use-credentials") {
            Some(CorsSettings::UseCredentials)
        } else {
            None
        }
    }
}
<|endoftext|>


<commit_before>//! A Rust interface for Objective-C blocks.
//!
//! For more information on the specifics of the block implementation, see
//! Clang's documentation: http://clang.llvm.org/docs/Block-ABI-Apple.html

use std::mem;
use std::ptr;
use libc::{c_int, c_ulong};

use runtime::Class;
use {Id, Message};

#[allow(improper_ctypes)]
#[link(name = "Foundation", kind = "framework")]
extern {
    static _NSConcreteStackBlock: Class;
}

/// An invoke function for a `Block`; this is the raw C function called by the
/// Objective-C runtime.
type BlockInvoke<A, R> = unsafe extern fn(*mut Block<A, R>, ...) -> R;

/// An invoke function for a `ConcreteBlock`; this is the raw C function called
/// by the Objective-C runtime.
pub type ConcreteBlockInvoke<A, R, C> =
    unsafe extern fn(*mut ConcreteBlock<A, R, C>, ...) -> R;

/// Types that may be used as the arguments to an Objective-C block.
pub trait BlockArguments {
    /// Calls the given `Block` with self as the arguments.
    fn call_block<R>(self, block: &Block<Self, R>) -> R;

    /// Returns an invoke function for a `ConcreteBlock` that takes this type
    /// of arguments.
    fn invoke_for_concrete_block<R, C: Clone>() -> ConcreteBlockInvoke<Self, R, C>;
}

macro_rules! block_args_impl(
    ($f:ident $(, $a:ident : $t:ident)*) => (
        impl<$($t),*> BlockArguments for ($($t,)*) {
            fn call_block<R>(self, block: &Block<($($t,)*), R>) -> R {
                let invoke: unsafe extern fn(*mut Block<($($t,)*), R> $(, $t)*) -> R = unsafe {
                    mem::transmute(block.invoke)
                };
                let ($($a,)*) = self;
                let block_ptr = block as *const _ as *mut _;
                unsafe {
                    invoke(block_ptr $(, $a)*)
                }
            }

            fn invoke_for_concrete_block<R, X: Clone>() ->
                    ConcreteBlockInvoke<($($t,)*), R, X> {
                unsafe extern fn $f<R, X: Clone $(, $t)*>(
                        block_ptr: *mut ConcreteBlock<($($t,)*), R, X>
                        $(, $a: $t)*) -> R {
                    let args = ($($a,)*);
                    let block = &*block_ptr;
                    (block.rust_invoke)(&block.context, args)
                }

                unsafe {
                    mem::transmute($f::<R, X $(, $t)*>)
                }
            }
        }
    );
);

block_args_impl!(concrete_block_invoke_args0);
block_args_impl!(concrete_block_invoke_args1, a: A);
block_args_impl!(concrete_block_invoke_args2, a: A, b: B);
block_args_impl!(concrete_block_invoke_args3, a: A, b: B, c: C);
block_args_impl!(concrete_block_invoke_args4, a: A, b: B, c: C, d: D);
block_args_impl!(concrete_block_invoke_args5, a: A, b: B, c: C, d: D, e: E);
block_args_impl!(concrete_block_invoke_args6, a: A, b: B, c: C, d: D, e: E, f: F);
block_args_impl!(concrete_block_invoke_args7, a: A, b: B, c: C, d: D, e: E, f: F, g: G);
block_args_impl!(concrete_block_invoke_args8, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
block_args_impl!(concrete_block_invoke_args9, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
block_args_impl!(concrete_block_invoke_args10, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
block_args_impl!(concrete_block_invoke_args11, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
block_args_impl!(concrete_block_invoke_args12, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);

/// An Objective-C block that takes arguments of `A` when called and
/// returns a value of `R`.
#[repr(C)]
pub struct Block<A: BlockArguments, R> {
    isa: *const Class,
    flags: c_int,
    _reserved: c_int,
    invoke: BlockInvoke<A, R>,
}

impl<A: BlockArguments, R> Block<A, R> {
    /// Call self with the given arguments.
    pub fn call(&self, args: A) -> R {
        args.call_block(self)
    }
}

impl<A: BlockArguments, R> Message for Block<A, R> { }

/// An Objective-C block whose size is known at compile time and may be
/// constructed on the stack.
#[repr(C)]
pub struct ConcreteBlock<A: BlockArguments, R, C> {
    base: Block<A, R>,
    descriptor: Box<BlockDescriptor<ConcreteBlock<A, R, C>>>,
    rust_invoke: fn (&C, A) -> R,
    context: C,
}

impl<A: BlockArguments, R, C: Clone> ConcreteBlock<A, R, C> {
    /// Constructs a `ConcreteBlock` with the given invoke function and context.
    /// When the block is called, it will return the value that results from
    /// calling the invoke function with a reference to its context.
    pub fn new(invoke: fn (&C, A) -> R, context: C) -> ConcreteBlock<A, R, C> {
        let extern_invoke: ConcreteBlockInvoke<A, R, C> =
            BlockArguments::invoke_for_concrete_block();
        ConcreteBlock {
            base: Block {
                isa: &_NSConcreteStackBlock,
                // 1 << 25 = BLOCK_HAS_COPY_DISPOSE
                flags: 1 << 25,
                _reserved: 0,
                invoke: unsafe { mem::transmute(extern_invoke) },
            },
            descriptor: box BlockDescriptor::<A, R, C>::new(),
            rust_invoke: invoke,
            context: context,
        }
    }
}

impl<A: BlockArguments, R, C> ConcreteBlock<A, R, C> {
    /// Copy self onto the heap.
    pub fn copy(self) -> Id<Block<A, R>> {
        unsafe {
            let block = msg_send![&self.base copy] as *mut Block<A, R>;
            Id::from_retained_ptr(block)
        }
    }
}

impl<A: BlockArguments, R, C: Clone> Clone for ConcreteBlock<A, R, C> {
    fn clone(&self) -> ConcreteBlock<A, R, C> {
        ConcreteBlock::new(self.rust_invoke, self.context.clone())
    }
}

impl<A: BlockArguments, R, C> Deref<Block<A, R>> for ConcreteBlock<A, R, C> {
    fn deref(&self) -> &Block<A, R> {
        &self.base
    }
}

unsafe extern fn block_context_dispose<A: BlockArguments, R, C>(
        block: &mut ConcreteBlock<A, R, C>) {
    // Read the block onto the stack and let it drop
    ptr::read(block);
}

unsafe extern fn block_context_copy<A: BlockArguments, R, C: Clone>(
        dst: &mut ConcreteBlock<A, R, C>, src: &ConcreteBlock<A, R, C>) {
    // Doesn't seem like the descriptor is supposed to change in this function,
    // but our descriptor isn't static (that's hard), so we just clone the box.
    ptr::write(&mut dst.descriptor, src.descriptor.clone());
    // The src block actually gets memmoved to the destination beforehand,
    // but we'll set the function pointer, too, to be safe.
    ptr::write(&mut dst.rust_invoke, src.rust_invoke);
    ptr::write(&mut dst.context, src.context.clone());
}

#[repr(C)]
struct BlockDescriptor<B> {
    _reserved: c_ulong,
    block_size: c_ulong,
    copy_helper: unsafe extern fn(&mut B, &B),
    dispose_helper: unsafe extern fn(&mut B),
}

impl<A: BlockArguments, R, C: Clone> BlockDescriptor<ConcreteBlock<A, R, C>> {
    fn new() -> BlockDescriptor<ConcreteBlock<A, R, C>> {
        BlockDescriptor {
            _reserved: 0,
            block_size: mem::size_of::<ConcreteBlock<A, R, C>>() as c_ulong,
            copy_helper: block_context_copy::<A, R, C>,
            dispose_helper: block_context_dispose::<A, R, C>,
        }
    }
}

impl<B> Copy for BlockDescriptor<B> { }

impl<B> Clone for BlockDescriptor<B> {
    fn clone(&self) -> BlockDescriptor<B> {
        *self
    }
}

#[cfg(test)]
mod tests {
    use Id;
    use objc_test_utils;
    use super::{Block, ConcreteBlock};

    fn get_int_block() -> &'static Block<(), int> {
        unsafe {
            &*(objc_test_utils::get_int_block() as *const _)
        }
    }

    fn get_int_block_with(i: int) -> Id<Block<(), int>> {
        unsafe {
            let ptr = objc_test_utils::get_int_block_with(i);
            Id::from_retained_ptr(ptr as *mut _)
        }
    }

    fn get_add_block() -> &'static Block<(int,), int> {
        unsafe {
            &*(objc_test_utils::get_add_block() as *const _)
        }
    }

    fn get_add_block_with(i: int) -> Id<Block<(int,), int>> {
        unsafe {
            let ptr = objc_test_utils::get_add_block_with(i);
            Id::from_retained_ptr(ptr as *mut _)
        }
    }

    fn invoke_int_block(block: &Block<(), int>) -> int {
        let ptr = block as *const _;
        unsafe {
            objc_test_utils::invoke_int_block(ptr as *const _)
        }
    }

    fn invoke_add_block(block: &Block<(int,), int>, a: int) -> int {
        let ptr = block as *const _;
        unsafe {
            objc_test_utils::invoke_add_block(ptr as *const _, a)
        }
    }

    #[test]
    fn test_call_block() {
        let block = get_int_block();
        assert!(block.call(()) == 7);

        let block = get_int_block_with(13);
        assert!(block.call(()) == 13);
    }

    #[test]
    fn test_call_block_args() {
        let block = get_add_block();
        assert!(block.call((2,)) == 9);

        let block = get_add_block_with(13);
        assert!(block.call((2,)) == 15);
    }

    #[test]
    fn test_create_block() {
        fn block_get_int(context: &int, _args: ()) -> int {
            *context
        }

        let block = ConcreteBlock::new(block_get_int, 13);
        let result = invoke_int_block(&*block);
        assert!(result == 13);
    }

    #[test]
    fn test_create_block_args() {
        fn block_add_int(context: &int, (a,): (int,)) -> int {
            a + *context
        }

        let block = ConcreteBlock::new(block_add_int, 5);
        let result = invoke_add_block(&*block, 6);
        assert!(result == 11);
    }

    #[test]
    fn test_concrete_block_clone() {
        fn block_get_string_len(context: &String, _args: ()) -> uint {
            context.len()
        }

        let s = "Hello!".into_string();
        let expected_len = s.len();
        let block = ConcreteBlock::new(block_get_string_len, s);
        assert!(block.call(()) == expected_len);

        let cloned = block.clone();
        assert!(cloned.call(()) == expected_len);

        drop(block);
        assert!(cloned.call(()) == expected_len);
    }

    #[test]
    fn test_concrete_block_copy() {
        fn block_get_string_len(context: &String, _args: ()) -> uint {
            context.len()
        }

        let s = "Hello!".into_string();
        let expected_len = s.len();
        let block = ConcreteBlock::new(block_get_string_len, s);
        assert!(block.call(()) == expected_len);

        let copied = block.copy();
        assert!(copied.call(()) == expected_len);
    }
}
<commit_msg>Remove Clone bound from ConcreteBlock.<commit_after>//! A Rust interface for Objective-C blocks.
//!
//! For more information on the specifics of the block implementation, see
//! Clang's documentation: http://clang.llvm.org/docs/Block-ABI-Apple.html

use std::mem;
use std::ptr;
use libc::{c_int, c_ulong};

use runtime::Class;
use {Id, Message};

#[allow(improper_ctypes)]
#[link(name = "Foundation", kind = "framework")]
extern {
    static _NSConcreteStackBlock: Class;
}

/// An invoke function for a `Block`; this is the raw C function called by the
/// Objective-C runtime.
type BlockInvoke<A, R> = unsafe extern fn(*mut Block<A, R>, ...) -> R;

/// An invoke function for a `ConcreteBlock`; this is the raw C function called
/// by the Objective-C runtime.
pub type ConcreteBlockInvoke<A, R, C> =
    unsafe extern fn(*mut ConcreteBlock<A, R, C>, ...) -> R;

/// Types that may be used as the arguments to an Objective-C block.
pub trait BlockArguments {
    /// Calls the given `Block` with self as the arguments.
    fn call_block<R>(self, block: &Block<Self, R>) -> R;

    /// Returns an invoke function for a `ConcreteBlock` that takes this type
    /// of arguments.
    fn invoke_for_concrete_block<R, C>() -> ConcreteBlockInvoke<Self, R, C>;
}

macro_rules! block_args_impl(
    ($f:ident $(, $a:ident : $t:ident)*) => (
        impl<$($t),*> BlockArguments for ($($t,)*) {
            fn call_block<R>(self, block: &Block<($($t,)*), R>) -> R {
                let invoke: unsafe extern fn(*mut Block<($($t,)*), R> $(, $t)*) -> R = unsafe {
                    mem::transmute(block.invoke)
                };
                let ($($a,)*) = self;
                let block_ptr = block as *const _ as *mut _;
                unsafe {
                    invoke(block_ptr $(, $a)*)
                }
            }

            fn invoke_for_concrete_block<R, X>() ->
                    ConcreteBlockInvoke<($($t,)*), R, X> {
                unsafe extern fn $f<R, X $(, $t)*>(
                        block_ptr: *mut ConcreteBlock<($($t,)*), R, X>
                        $(, $a: $t)*) -> R {
                    let args = ($($a,)*);
                    let block = &*block_ptr;
                    (block.rust_invoke)(&block.context, args)
                }

                unsafe {
                    mem::transmute($f::<R, X $(, $t)*>)
                }
            }
        }
    );
);

block_args_impl!(concrete_block_invoke_args0);
block_args_impl!(concrete_block_invoke_args1, a: A);
block_args_impl!(concrete_block_invoke_args2, a: A, b: B);
block_args_impl!(concrete_block_invoke_args3, a: A, b: B, c: C);
block_args_impl!(concrete_block_invoke_args4, a: A, b: B, c: C, d: D);
block_args_impl!(concrete_block_invoke_args5, a: A, b: B, c: C, d: D, e: E);
block_args_impl!(concrete_block_invoke_args6, a: A, b: B, c: C, d: D, e: E, f: F);
block_args_impl!(concrete_block_invoke_args7, a: A, b: B, c: C, d: D, e: E, f: F, g: G);
block_args_impl!(concrete_block_invoke_args8, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
block_args_impl!(concrete_block_invoke_args9, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
block_args_impl!(concrete_block_invoke_args10, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
block_args_impl!(concrete_block_invoke_args11, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
block_args_impl!(concrete_block_invoke_args12, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);

/// An Objective-C block that takes arguments of `A` when called and
/// returns a value of `R`.
#[repr(C)]
pub struct Block<A: BlockArguments, R> {
    isa: *const Class,
    flags: c_int,
    _reserved: c_int,
    invoke: BlockInvoke<A, R>,
}

impl<A: BlockArguments, R> Block<A, R> {
    /// Call self with the given arguments.
    pub fn call(&self, args: A) -> R {
        args.call_block(self)
    }
}

impl<A: BlockArguments, R> Message for Block<A, R> { }

/// An Objective-C block whose size is known at compile time and may be
/// constructed on the stack.
#[repr(C)]
pub struct ConcreteBlock<A: BlockArguments, R, C> {
    base: Block<A, R>,
    descriptor: Box<BlockDescriptor<ConcreteBlock<A, R, C>>>,
    rust_invoke: fn (&C, A) -> R,
    context: C,
}

impl<A: BlockArguments, R, C> ConcreteBlock<A, R, C> {
    /// Constructs a `ConcreteBlock` with the given invoke function and context.
    /// When the block is called, it will return the value that results from
    /// calling the invoke function with a reference to its context.
    pub fn new(invoke: fn (&C, A) -> R, context: C) -> ConcreteBlock<A, R, C> {
        let extern_invoke: ConcreteBlockInvoke<A, R, C> =
            BlockArguments::invoke_for_concrete_block();
        ConcreteBlock {
            base: Block {
                isa: &_NSConcreteStackBlock,
                // 1 << 25 = BLOCK_HAS_COPY_DISPOSE
                flags: 1 << 25,
                _reserved: 0,
                invoke: unsafe { mem::transmute(extern_invoke) },
            },
            descriptor: box BlockDescriptor::<A, R, C>::new(),
            rust_invoke: invoke,
            context: context,
        }
    }

    /// Copy self onto the heap.
    pub fn copy(self) -> Id<Block<A, R>> {
        unsafe {
            let block = msg_send![&self.base copy] as *mut Block<A, R>;
            // At this point, our copy helper has been run so the block will
            // be moved to the heap and we can forget the original block
            // because the heap block will drop in our dispose helper.
            mem::forget(self);
            Id::from_retained_ptr(block)
        }
    }
}

impl<A: BlockArguments, R, C: Clone> Clone for ConcreteBlock<A, R, C> {
    fn clone(&self) -> ConcreteBlock<A, R, C> {
        ConcreteBlock::new(self.rust_invoke, self.context.clone())
    }
}

impl<A: BlockArguments, R, C> Deref<Block<A, R>> for ConcreteBlock<A, R, C> {
    fn deref(&self) -> &Block<A, R> {
        &self.base
    }
}

unsafe extern fn block_context_dispose<A: BlockArguments, R, C>(
        block: &mut ConcreteBlock<A, R, C>) {
    // Read the block onto the stack and let it drop
    ptr::read(block);
}

unsafe extern fn block_context_copy<A: BlockArguments, R, C>(
        _dst: &mut ConcreteBlock<A, R, C>, _src: &ConcreteBlock<A, R, C>) {
    // The runtime memmoves the src block into the dst block, nothing to do
}

#[repr(C)]
struct BlockDescriptor<B> {
    _reserved: c_ulong,
    block_size: c_ulong,
    copy_helper: unsafe extern fn(&mut B, &B),
    dispose_helper: unsafe extern fn(&mut B),
}

impl<A: BlockArguments, R, C> BlockDescriptor<ConcreteBlock<A, R, C>> {
    fn new() -> BlockDescriptor<ConcreteBlock<A, R, C>> {
        BlockDescriptor {
            _reserved: 0,
            block_size: mem::size_of::<ConcreteBlock<A, R, C>>() as c_ulong,
            copy_helper: block_context_copy::<A, R, C>,
            dispose_helper: block_context_dispose::<A, R, C>,
        }
    }
}

#[cfg(test)]
mod tests {
    use Id;
    use objc_test_utils;
    use super::{Block, ConcreteBlock};

    fn get_int_block() -> &'static Block<(), int> {
        unsafe {
            &*(objc_test_utils::get_int_block() as *const _)
        }
    }

    fn get_int_block_with(i: int) -> Id<Block<(), int>> {
        unsafe {
            let ptr = objc_test_utils::get_int_block_with(i);
            Id::from_retained_ptr(ptr as *mut _)
        }
    }

    fn get_add_block() -> &'static Block<(int,), int> {
        unsafe {
            &*(objc_test_utils::get_add_block() as *const _)
        }
    }

    fn get_add_block_with(i: int) -> Id<Block<(int,), int>> {
        unsafe {
            let ptr = objc_test_utils::get_add_block_with(i);
            Id::from_retained_ptr(ptr as *mut _)
        }
    }

    fn invoke_int_block(block: &Block<(), int>) -> int {
        let ptr = block as *const _;
        unsafe {
            objc_test_utils::invoke_int_block(ptr as *const _)
        }
    }

    fn invoke_add_block(block: &Block<(int,), int>, a: int) -> int {
        let ptr = block as *const _;
        unsafe {
            objc_test_utils::invoke_add_block(ptr as *const _, a)
        }
    }

    #[test]
    fn test_call_block() {
        let block = get_int_block();
        assert!(block.call(()) == 7);

        let block = get_int_block_with(13);
        assert!(block.call(()) == 13);
    }

    #[test]
    fn test_call_block_args() {
        let block = get_add_block();
        assert!(block.call((2,)) == 9);

        let block = get_add_block_with(13);
        assert!(block.call((2,)) == 15);
    }

    #[test]
    fn test_create_block() {
        fn block_get_int(context: &int, _args: ()) -> int {
            *context
        }

        let block = ConcreteBlock::new(block_get_int, 13);
        let result = invoke_int_block(&*block);
        assert!(result == 13);
    }

    #[test]
    fn test_create_block_args() {
        fn block_add_int(context: &int, (a,): (int,)) -> int {
            a + *context
        }

        let block = ConcreteBlock::new(block_add_int, 5);
        let result = invoke_add_block(&*block, 6);
        assert!(result == 11);
    }

    #[test]
    fn test_concrete_block_clone() {
        fn block_get_string_len(context: &String, _args: ()) -> uint {
            context.len()
        }

        let s = "Hello!".into_string();
        let expected_len = s.len();
        let block = ConcreteBlock::new(block_get_string_len, s);
        assert!(block.call(()) == expected_len);

        let cloned = block.clone();
        assert!(cloned.call(()) == expected_len);

        drop(block);
        assert!(cloned.call(()) == expected_len);
    }

    #[test]
    fn test_concrete_block_copy() {
        fn block_get_string_len(context: &String, _args: ()) -> uint {
            context.len()
        }

        let s = "Hello!".into_string();
        let expected_len = s.len();
        let block = ConcreteBlock::new(block_get_string_len, s);
        assert!(block.call(()) == expected_len);

        let copied = block.copy();
        assert!(copied.call(()) == expected_len);
    }
}
<|endoftext|>