<commit_before>//! Implements ModuleEnvironment for cranelift-wasm. Code derived from cranelift-wasm/environ/dummy.rs
use crate::error::Error;
use crate::pointer::NATIVE_POINTER;
use cranelift_codegen::entity::{entity_impl, EntityRef, PrimaryMap, SecondaryMap};
use cranelift_codegen::ir;
use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_wasm::{
    FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, ModuleEnvironment, ModuleTranslationState,
    SignatureIndex, Table, TableElementType, TableIndex, TargetEnvironment, WasmResult,
};
use lucet_module::UniqueSignatureIndex;
use std::collections::{hash_map::Entry, HashMap};

/// UniqueFuncIndex names a function after merging duplicate function declarations to a single
/// identifier, whereas FuncIndex is maintained by Cranelift and may have multiple indices referring
/// to a single function in the resulting artifact.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct UniqueFuncIndex(u32);
entity_impl!(UniqueFuncIndex);

#[derive(Debug, Clone)]
pub struct Exportable<'a, T> {
    pub entity: T,
    pub export_names: Vec<&'a str>,
}

impl<'a, T> Exportable<'a, T> {
    pub fn new(entity: T) -> Self {
        Self {
            entity,
            export_names: Vec::new(),
        }
    }
    pub fn push_export(&mut self, name: &'a str) {
        self.export_names.push(name);
    }
}

#[derive(Debug, Clone)]
pub struct TableElems {
    pub base: Option<GlobalIndex>,
    pub offset: usize,
    pub elements: Box<[UniqueFuncIndex]>,
}

#[derive(Debug, Clone)]
pub struct DataInitializer<'a> {
    pub base: Option<GlobalIndex>,
    pub offset: usize,
    pub data: &'a [u8],
}

pub struct ModuleInfo<'a> {
    /// Target description used for codegen
    pub target_config: TargetFrontendConfig,
    /// This mapping lets us merge duplicate types (permitted by the wasm spec) as they're
    /// declared.
    pub signature_mapping: PrimaryMap<SignatureIndex, UniqueSignatureIndex>,
    /// Provided by `declare_signature`
    pub signatures: PrimaryMap<UniqueSignatureIndex, ir::Signature>,
    /// Provided by `declare_func_import`
    pub imported_funcs: PrimaryMap<UniqueFuncIndex, (&'a str, &'a str)>,
    /// Provided by `declare_global_import`
    pub imported_globals: PrimaryMap<GlobalIndex, (&'a str, &'a str)>,
    /// Provided by `declare_table_import`
    pub imported_tables: PrimaryMap<TableIndex, (&'a str, &'a str)>,
    /// Provided by `declare_memory_import`
    pub imported_memories: PrimaryMap<MemoryIndex, (&'a str, &'a str)>,
    /// This mapping lets us merge duplicate functions (for example, multiple import declarations)
    /// as they're declared.
    pub function_mapping: PrimaryMap<FuncIndex, UniqueFuncIndex>,
    /// Function signatures: imported and local
    pub functions: PrimaryMap<UniqueFuncIndex, Exportable<'a, SignatureIndex>>,
    /// Function names.
    pub function_names: SecondaryMap<UniqueFuncIndex, &'a str>,
    /// Provided by `declare_table`
    pub tables: PrimaryMap<TableIndex, Exportable<'a, Table>>,
    /// Provided by `declare_memory`
    pub memories: PrimaryMap<MemoryIndex, Exportable<'a, Memory>>,
    /// Provided by `declare_global`
    pub globals: PrimaryMap<GlobalIndex, Exportable<'a, Global>>,
    /// Provided by `declare_start_func`
    pub start_func: Option<UniqueFuncIndex>,

    /// Function bodies: local only
    pub function_bodies: HashMap<UniqueFuncIndex, (&'a [u8], usize)>,

    /// Table elements: local only
    pub table_elems: HashMap<TableIndex, Vec<TableElems>>,

    /// Data initializers: local only
    pub data_initializers: HashMap<MemoryIndex, Vec<DataInitializer<'a>>>,
}

impl<'a> ModuleInfo<'a> {
    pub fn new(target_config: TargetFrontendConfig) -> Self {
        Self {
            target_config,
            signature_mapping: PrimaryMap::new(),
            signatures: PrimaryMap::new(),
            imported_funcs: PrimaryMap::new(),
            imported_globals: PrimaryMap::new(),
            imported_tables: PrimaryMap::new(),
            imported_memories: PrimaryMap::new(),
            function_mapping: PrimaryMap::new(),
            functions: PrimaryMap::new(),
            function_names: SecondaryMap::new(),
            tables: PrimaryMap::new(),
            memories: PrimaryMap::new(),
            globals: PrimaryMap::new(),
            start_func: None,
            function_bodies: HashMap::new(),
            table_elems: HashMap::new(),
            data_initializers: HashMap::new(),
        }
    }

    pub fn signature_for_function(&self, func_index: UniqueFuncIndex) -> &ir::Signature {
        // UniqueFuncIndex are valid (or the caller has very bad data)
        let sigidx = self.functions.get(func_index).unwrap().entity;

        self.signature_by_id(sigidx)
    }

    pub fn signature_by_id(&self, sig_idx: SignatureIndex) -> &ir::Signature {
        // All signatures map to some unique signature index
        let unique_sig_idx = self.signature_mapping.get(sig_idx).unwrap();
        // Unique signature indices are valid (or we're in some deeply bad state)
        self.signatures.get(*unique_sig_idx).unwrap()
    }

    pub fn declare_func_with_sig(
        &mut self,
        sig: ir::Signature,
    ) -> Result<(UniqueFuncIndex, SignatureIndex), Error> {
        let new_sigidx = SignatureIndex::from_u32(self.signature_mapping.len() as u32);
        self.declare_signature(sig)?;
        let new_funcidx = UniqueFuncIndex::from_u32(self.functions.len() as u32);
        self.declare_func_type(new_sigidx)?;
        Ok((new_funcidx, new_sigidx))
    }
}

impl<'a> TargetEnvironment for ModuleInfo<'a> {
    fn target_config(&self) -> TargetFrontendConfig {
        self.target_config
    }
}

impl<'a> ModuleEnvironment<'a> for ModuleInfo<'a> {
    fn declare_signature(&mut self, mut sig: ir::Signature) -> WasmResult<()> {
        sig.params.insert(
            0,
            ir::AbiParam::special(NATIVE_POINTER, ir::ArgumentPurpose::VMContext),
        );

        let match_key = self
            .signatures
            .iter()
            .find(|(_, v)| *v == &sig)
            .map(|(key, _)| key)
            .unwrap_or_else(|| {
                let lucet_sig_ix = UniqueSignatureIndex::from_u32(self.signatures.len() as u32);
                self.signatures.push(sig);
                lucet_sig_ix
            });

        self.signature_mapping.push(match_key);
        Ok(())
    }

    fn declare_func_import(
        &mut self,
        sig_index: SignatureIndex,
        module: &'a str,
        field: &'a str,
    ) -> WasmResult<()> {
        debug_assert_eq!(
            self.functions.len(),
            self.imported_funcs.len(),
            "import functions are declared first"
        );

        let unique_fn_index = self
            .imported_funcs
            .iter()
            .find(|(_, v)| *v == &(module, field))
            .map(|(key, _)| key)
            .unwrap_or_else(|| {
                self.functions.push(Exportable::new(sig_index));
                self.imported_funcs.push((module, field));
                UniqueFuncIndex::from_u32(self.functions.len() as u32 - 1)
            });

        self.function_mapping.push(unique_fn_index);
        Ok(())
    }

    fn declare_global_import(
        &mut self,
        global: Global,
        module: &'a str,
        field: &'a str,
    ) -> WasmResult<()> {
        debug_assert_eq!(
            self.globals.len(),
            self.imported_globals.len(),
            "import globals are declared first"
        );
        self.globals.push(Exportable::new(global));
        self.imported_globals.push((module, field));
        Ok(())
    }

    fn declare_table_import(
        &mut self,
        table: Table,
        module: &'a str,
        field: &'a str,
    ) -> WasmResult<()> {
        debug_assert_eq!(
            self.tables.len(),
            self.imported_tables.len(),
            "import tables are declared first"
        );
        self.tables.push(Exportable::new(table));
        self.imported_tables.push((module, field));
        Ok(())
    }

    fn declare_memory_import(
        &mut self,
        memory: Memory,
        module: &'a str,
        field: &'a str,
    ) -> WasmResult<()> {
        debug_assert_eq!(
            self.memories.len(),
            self.imported_memories.len(),
            "import memories are declared first"
        );
        self.data_initializers
            .insert(MemoryIndex::new(self.memories.len()), vec![]);
        self.memories.push(Exportable::new(memory));
        self.imported_memories.push((module, field));
        Ok(())
    }

    fn declare_func_type(&mut self, sig_index: SignatureIndex) -> WasmResult<()> {
        self.functions.push(Exportable::new(sig_index));
        self.function_mapping
            .push(UniqueFuncIndex::from_u32(self.functions.len() as u32 - 1));
        Ok(())
    }

    fn declare_table(&mut self, table: Table) -> WasmResult<()> {
        self.table_elems
            .insert(TableIndex::new(self.tables.len()), vec![]);
        self.tables.push(Exportable::new(table));
        Ok(())
    }

    fn declare_memory(&mut self, memory: Memory) -> WasmResult<()> {
        self.data_initializers
            .insert(MemoryIndex::new(self.memories.len()), vec![]);
        self.memories.push(Exportable::new(memory));
        Ok(())
    }

    fn declare_global(&mut self, global: Global) -> WasmResult<()> {
        self.globals.push(Exportable::new(global));
        Ok(())
    }

    fn declare_func_export(&mut self, func_index: FuncIndex, name: &'a str) -> WasmResult<()> {
        let unique_func_index = *self
            .function_mapping
            .get(func_index)
            .expect("function indices are valid");
        self.functions
            .get_mut(unique_func_index)
            .expect("export of declared function")
            .push_export(name);
        Ok(())
    }

    fn declare_table_export(&mut self, table_index: TableIndex, name: &'a str) -> WasmResult<()> {
        self.tables
            .get_mut(table_index)
            .expect("export of declared table")
            .push_export(name);
        Ok(())
    }

    fn declare_memory_export(
        &mut self,
        memory_index: MemoryIndex,
        name: &'a str,
    ) -> WasmResult<()> {
        self.memories
            .get_mut(memory_index)
            .expect("export of declared memory")
            .push_export(name);
        Ok(())
    }

    fn declare_global_export(
        &mut self,
        global_index: GlobalIndex,
        name: &'a str,
    ) -> WasmResult<()> {
        self.globals
            .get_mut(global_index)
            .expect("export of declared global")
            .push_export(name);
        Ok(())
    }

    fn declare_start_func(&mut self, func_index: FuncIndex) -> WasmResult<()> {
        let unique_func_index = *self
            .function_mapping
            .get(func_index)
            .expect("function indices are valid");
        debug_assert!(
            self.start_func.is_none(),
            "start func can only be defined once"
        );
        self.start_func = Some(unique_func_index);
        Ok(())
    }

    fn define_function_body(
        &mut self,
        _module_translation_state: &ModuleTranslationState,
        body_bytes: &'a [u8],
        body_offset: usize,
    ) -> WasmResult<()> {
        let func_index =
            UniqueFuncIndex::new(self.imported_funcs.len() + self.function_bodies.len());
        self.function_bodies
            .insert(func_index, (body_bytes, body_offset));
        Ok(())
    }

    fn declare_table_elements(
        &mut self,
        table_index: TableIndex,
        base: Option<GlobalIndex>,
        offset: usize,
        elements: Box<[FuncIndex]>,
    ) -> WasmResult<()> {
        let elements_vec: Vec<FuncIndex> = elements.into();
        let uniquified_elements = elements_vec
            .into_iter()
            .map(|fn_idx| {
                *self
                    .function_mapping
                    .get(fn_idx)
                    .expect("function indices are valid")
            })
            .collect();
        let table_elems = TableElems {
            base,
            offset,
            elements: uniquified_elements,
        };
        match self.table_elems.entry(table_index) {
            Entry::Occupied(mut occ) => occ.get_mut().push(table_elems),
            Entry::Vacant(vac) => {
                if self.tables.is_empty() && table_index == TableIndex::new(0) {
                    let table = Table {
                        ty: TableElementType::Func,
                        minimum: 0,
                        maximum: None,
                    };
                    self.tables.push(Exportable::new(table));
                    vac.insert(vec![table_elems]);
                } else {
                    panic!("creation of elements for undeclared table! only table 0 is implicitly declared")
                    // Do we implicitly declare them all???? i sure hope not
                }
            }
        }
        Ok(())
    }

    fn declare_data_initialization(
        &mut self,
        memory_index: MemoryIndex,
        base: Option<GlobalIndex>,
        offset: usize,
        data: &'a [u8],
    ) -> WasmResult<()> {
        let data_init = DataInitializer { base, offset, data };
        match self.data_initializers.entry(memory_index) {
            Entry::Occupied(mut occ) => {
                occ.get_mut().push(data_init);
            }
            Entry::Vacant(_) => panic!(
                "data initializer for undeclared memory {:?}: {:?}",
                memory_index, data_init
            ),
        }
        Ok(())
    }

    fn declare_func_name(&mut self, func_index: FuncIndex, name: &'a str) -> WasmResult<()> {
        let unique_func_index = *self
            .function_mapping
            .get(func_index)
            .expect("function indices are valid");
        self.function_names[unique_func_index] = name;
        Ok(())
    }
}
<commit_msg>add unimplemented new ModuleEnvironment methods<commit_after>//! Implements ModuleEnvironment for cranelift-wasm. Code derived from cranelift-wasm/environ/dummy.rs
use crate::error::Error;
use crate::pointer::NATIVE_POINTER;
use cranelift_codegen::entity::{entity_impl, EntityRef, PrimaryMap, SecondaryMap};
use cranelift_codegen::ir;
use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_wasm::{
    DataIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, ModuleEnvironment, ModuleTranslationState,
    SignatureIndex, Table, TableElementType, TableIndex, TargetEnvironment, WasmResult,
};
use lucet_module::UniqueSignatureIndex;
use std::collections::{hash_map::Entry, HashMap};

/// UniqueFuncIndex names a function after merging duplicate function declarations to a single
/// identifier, whereas FuncIndex is maintained by Cranelift and may have multiple indices referring
/// to a single function in the resulting artifact.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct UniqueFuncIndex(u32);
entity_impl!(UniqueFuncIndex);

#[derive(Debug, Clone)]
pub struct Exportable<'a, T> {
    pub entity: T,
    pub export_names: Vec<&'a str>,
}

impl<'a, T> Exportable<'a, T> {
    pub fn new(entity: T) -> Self {
        Self {
            entity,
            export_names: Vec::new(),
        }
    }
    pub fn push_export(&mut self, name: &'a str) {
        self.export_names.push(name);
    }
}

#[derive(Debug, Clone)]
pub struct TableElems {
    pub base: Option<GlobalIndex>,
    pub offset: usize,
    pub elements: Box<[UniqueFuncIndex]>,
}

#[derive(Debug, Clone)]
pub struct DataInitializer<'a> {
    pub base: Option<GlobalIndex>,
    pub offset: usize,
    pub data: &'a [u8],
}

pub struct ModuleInfo<'a> {
    /// Target description used for codegen
    pub target_config: TargetFrontendConfig,
    /// This mapping lets us merge duplicate types (permitted by the wasm spec) as they're
    /// declared.
    pub signature_mapping: PrimaryMap<SignatureIndex, UniqueSignatureIndex>,
    /// Provided by `declare_signature`
    pub signatures: PrimaryMap<UniqueSignatureIndex, ir::Signature>,
    /// Provided by `declare_func_import`
    pub imported_funcs: PrimaryMap<UniqueFuncIndex, (&'a str, &'a str)>,
    /// Provided by `declare_global_import`
    pub imported_globals: PrimaryMap<GlobalIndex, (&'a str, &'a str)>,
    /// Provided by `declare_table_import`
    pub imported_tables: PrimaryMap<TableIndex, (&'a str, &'a str)>,
    /// Provided by `declare_memory_import`
    pub imported_memories: PrimaryMap<MemoryIndex, (&'a str, &'a str)>,
    /// This mapping lets us merge duplicate functions (for example, multiple import declarations)
    /// as they're declared.
    pub function_mapping: PrimaryMap<FuncIndex, UniqueFuncIndex>,
    /// Function signatures: imported and local
    pub functions: PrimaryMap<UniqueFuncIndex, Exportable<'a, SignatureIndex>>,
    /// Function names.
    pub function_names: SecondaryMap<UniqueFuncIndex, &'a str>,
    /// Provided by `declare_table`
    pub tables: PrimaryMap<TableIndex, Exportable<'a, Table>>,
    /// Provided by `declare_memory`
    pub memories: PrimaryMap<MemoryIndex, Exportable<'a, Memory>>,
    /// Provided by `declare_global`
    pub globals: PrimaryMap<GlobalIndex, Exportable<'a, Global>>,
    /// Provided by `declare_start_func`
    pub start_func: Option<UniqueFuncIndex>,

    /// Function bodies: local only
    pub function_bodies: HashMap<UniqueFuncIndex, (&'a [u8], usize)>,

    /// Table elements: local only
    pub table_elems: HashMap<TableIndex, Vec<TableElems>>,

    /// Data initializers: local only
    pub data_initializers: HashMap<MemoryIndex, Vec<DataInitializer<'a>>>,
}

impl<'a> ModuleInfo<'a> {
    pub fn new(target_config: TargetFrontendConfig) -> Self {
        Self {
            target_config,
            signature_mapping: PrimaryMap::new(),
            signatures: PrimaryMap::new(),
            imported_funcs: PrimaryMap::new(),
            imported_globals: PrimaryMap::new(),
            imported_tables: PrimaryMap::new(),
            imported_memories: PrimaryMap::new(),
            function_mapping: PrimaryMap::new(),
            functions: PrimaryMap::new(),
            function_names: SecondaryMap::new(),
            tables: PrimaryMap::new(),
            memories: PrimaryMap::new(),
            globals: PrimaryMap::new(),
            start_func: None,
            function_bodies: HashMap::new(),
            table_elems: HashMap::new(),
            data_initializers: HashMap::new(),
        }
    }

    pub fn signature_for_function(&self, func_index: UniqueFuncIndex) -> &ir::Signature {
        // UniqueFuncIndex are valid (or the caller has very bad data)
        let sigidx = self.functions.get(func_index).unwrap().entity;

        self.signature_by_id(sigidx)
    }

    pub fn signature_by_id(&self, sig_idx: SignatureIndex) -> &ir::Signature {
        // All signatures map to some unique signature index
        let unique_sig_idx = self.signature_mapping.get(sig_idx).unwrap();
        // Unique signature indices are valid (or we're in some deeply bad state)
        self.signatures.get(*unique_sig_idx).unwrap()
    }

    pub fn declare_func_with_sig(
        &mut self,
        sig: ir::Signature,
    ) -> Result<(UniqueFuncIndex, SignatureIndex), Error> {
        let new_sigidx = SignatureIndex::from_u32(self.signature_mapping.len() as u32);
        self.declare_signature(sig)?;
        let new_funcidx = UniqueFuncIndex::from_u32(self.functions.len() as u32);
        self.declare_func_type(new_sigidx)?;
        Ok((new_funcidx, new_sigidx))
    }
}

impl<'a> TargetEnvironment for ModuleInfo<'a> {
    fn target_config(&self) -> TargetFrontendConfig {
        self.target_config
    }
}

impl<'a> ModuleEnvironment<'a> for ModuleInfo<'a> {
    fn declare_signature(&mut self, mut sig: ir::Signature) -> WasmResult<()> {
        sig.params.insert(
            0,
            ir::AbiParam::special(NATIVE_POINTER, ir::ArgumentPurpose::VMContext),
        );

        let match_key = self
            .signatures
            .iter()
            .find(|(_, v)| *v == &sig)
            .map(|(key, _)| key)
            .unwrap_or_else(|| {
                let lucet_sig_ix = UniqueSignatureIndex::from_u32(self.signatures.len() as u32);
                self.signatures.push(sig);
                lucet_sig_ix
            });

        self.signature_mapping.push(match_key);
        Ok(())
    }

    fn declare_func_import(
        &mut self,
        sig_index: SignatureIndex,
        module: &'a str,
        field: &'a str,
    ) -> WasmResult<()> {
        debug_assert_eq!(
            self.functions.len(),
            self.imported_funcs.len(),
            "import functions are declared first"
        );

        let unique_fn_index = self
            .imported_funcs
            .iter()
            .find(|(_, v)| *v == &(module, field))
            .map(|(key, _)| key)
            .unwrap_or_else(|| {
                self.functions.push(Exportable::new(sig_index));
                self.imported_funcs.push((module, field));
                UniqueFuncIndex::from_u32(self.functions.len() as u32 - 1)
            });

        self.function_mapping.push(unique_fn_index);
        Ok(())
    }

    fn declare_global_import(
        &mut self,
        global: Global,
        module: &'a str,
        field: &'a str,
    ) -> WasmResult<()> {
        debug_assert_eq!(
            self.globals.len(),
            self.imported_globals.len(),
            "import globals are declared first"
        );
        self.globals.push(Exportable::new(global));
        self.imported_globals.push((module, field));
        Ok(())
    }

    fn declare_table_import(
        &mut self,
        table: Table,
        module: &'a str,
        field: &'a str,
    ) -> WasmResult<()> {
        debug_assert_eq!(
            self.tables.len(),
            self.imported_tables.len(),
            "import tables are declared first"
        );
        self.tables.push(Exportable::new(table));
        self.imported_tables.push((module, field));
        Ok(())
    }

    fn declare_memory_import(
        &mut self,
        memory: Memory,
        module: &'a str,
        field: &'a str,
    ) -> WasmResult<()> {
        debug_assert_eq!(
            self.memories.len(),
            self.imported_memories.len(),
            "import memories are declared first"
        );
        self.data_initializers
            .insert(MemoryIndex::new(self.memories.len()), vec![]);
        self.memories.push(Exportable::new(memory));
        self.imported_memories.push((module, field));
        Ok(())
    }

    fn declare_func_type(&mut self, sig_index: SignatureIndex) -> WasmResult<()> {
        self.functions.push(Exportable::new(sig_index));
        self.function_mapping
            .push(UniqueFuncIndex::from_u32(self.functions.len() as u32 - 1));
        Ok(())
    }

    fn declare_table(&mut self, table: Table) -> WasmResult<()> {
        self.table_elems
            .insert(TableIndex::new(self.tables.len()), vec![]);
        self.tables.push(Exportable::new(table));
        Ok(())
    }

    fn declare_memory(&mut self, memory: Memory) -> WasmResult<()> {
        self.data_initializers
            .insert(MemoryIndex::new(self.memories.len()), vec![]);
        self.memories.push(Exportable::new(memory));
        Ok(())
    }

    fn declare_global(&mut self, global: Global) -> WasmResult<()> {
        self.globals.push(Exportable::new(global));
        Ok(())
    }

    fn declare_func_export(&mut self, func_index: FuncIndex, name: &'a str) -> WasmResult<()> {
        let unique_func_index = *self
            .function_mapping
            .get(func_index)
            .expect("function indices are valid");
        self.functions
            .get_mut(unique_func_index)
            .expect("export of declared function")
            .push_export(name);
        Ok(())
    }

    fn declare_table_export(&mut self, table_index: TableIndex, name: &'a str) -> WasmResult<()> {
        self.tables
            .get_mut(table_index)
            .expect("export of declared table")
            .push_export(name);
        Ok(())
    }

    fn declare_memory_export(
        &mut self,
        memory_index: MemoryIndex,
        name: &'a str,
    ) -> WasmResult<()> {
        self.memories
            .get_mut(memory_index)
            .expect("export of declared memory")
            .push_export(name);
        Ok(())
    }

    fn declare_global_export(
        &mut self,
        global_index: GlobalIndex,
        name: &'a str,
    ) -> WasmResult<()> {
        self.globals
            .get_mut(global_index)
            .expect("export of declared global")
            .push_export(name);
        Ok(())
    }

    fn declare_start_func(&mut self, func_index: FuncIndex) -> WasmResult<()> {
        let unique_func_index = *self
            .function_mapping
            .get(func_index)
            .expect("function indices are valid");
        debug_assert!(
            self.start_func.is_none(),
            "start func can only be defined once"
        );
        self.start_func = Some(unique_func_index);
        Ok(())
    }

    fn define_function_body(
        &mut self,
        _module_translation_state: &ModuleTranslationState,
        body_bytes: &'a [u8],
        body_offset: usize,
    ) -> WasmResult<()> {
        let func_index =
            UniqueFuncIndex::new(self.imported_funcs.len() + self.function_bodies.len());
        self.function_bodies
            .insert(func_index, (body_bytes, body_offset));
        Ok(())
    }

    fn declare_table_elements(
        &mut self,
        table_index: TableIndex,
        base: Option<GlobalIndex>,
        offset: usize,
        elements: Box<[FuncIndex]>,
    ) -> WasmResult<()> {
        let elements_vec: Vec<FuncIndex> = elements.into();
        let uniquified_elements = elements_vec
            .into_iter()
            .map(|fn_idx| {
                *self
                    .function_mapping
                    .get(fn_idx)
                    .expect("function indices are valid")
            })
            .collect();
        let table_elems = TableElems {
            base,
            offset,
            elements: uniquified_elements,
        };
        match self.table_elems.entry(table_index) {
            Entry::Occupied(mut occ) => occ.get_mut().push(table_elems),
            Entry::Vacant(vac) => {
                if self.tables.is_empty() && table_index == TableIndex::new(0) {
                    let table = Table {
                        ty: TableElementType::Func,
                        minimum: 0,
                        maximum: None,
                    };
                    self.tables.push(Exportable::new(table));
                    vac.insert(vec![table_elems]);
                } else {
                    panic!("creation of elements for undeclared table! only table 0 is implicitly declared")
                    // Do we implicitly declare them all???? i sure hope not
                }
            }
        }
        Ok(())
    }

    fn declare_data_initialization(
        &mut self,
        memory_index: MemoryIndex,
        base: Option<GlobalIndex>,
        offset: usize,
        data: &'a [u8],
    ) -> WasmResult<()> {
        let data_init = DataInitializer { base, offset, data };
        match self.data_initializers.entry(memory_index) {
            Entry::Occupied(mut occ) => {
                occ.get_mut().push(data_init);
            }
            Entry::Vacant(_) => panic!(
                "data initializer for undeclared memory {:?}: {:?}",
                memory_index, data_init
            ),
        }
        Ok(())
    }

    fn declare_func_name(&mut self, func_index: FuncIndex, name: &'a str) -> WasmResult<()> {
        let unique_func_index = *self
            .function_mapping
            .get(func_index)
            .expect("function indices are valid");
        self.function_names[unique_func_index] = name;
        Ok(())
    }

    fn declare_passive_element(&mut self, _index: ElemIndex, _elements: Box<[FuncIndex]>) -> WasmResult<()> {
        unimplemented!();
    }

    fn declare_passive_data(&mut self, _data_index: DataIndex, _data: &'a [u8]) -> WasmResult<()> {
        unimplemented!();
    }
}
<|endoftext|>


<commit_before>pub mod msg;
pub mod helpers;
pub mod error;
pub mod genmsg;

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;

#[macro_export]
macro_rules! rosmsg_main {
    ($($msg:expr),*)=> {
        fn main() {
            $crate::build_tools::depend_on_messages(&[
            $(
                $msg,
            )*
            ]);
        }
    }
}

#[macro_export]
macro_rules! rosmsg_include {
    () => {include!(concat!(env!("OUT_DIR"), "/msg.rs"));}
}

pub fn depend_on_messages(messages: &[&str]) {
    let variables = ["CMAKE_PREFIX_PATH", "ROSRUST_MSG_PATH"]
        .iter()
        .filter_map(|v| env::var(v).ok())
        .collect::<Vec<String>>();
    let paths = variables
        .iter()
        .flat_map(|v| v.split(":"))
        .collect::<Vec<&str>>();
    let output = genmsg::depend_on_messages(paths.as_slice(), messages).unwrap();
    let out_dir = env::var("OUT_DIR").unwrap();
    let dest_path = Path::new(&out_dir).join("msg.rs");
    let mut f = File::create(&dest_path).unwrap();
    write!(f, "{}", output).unwrap();
}
<commit_msg>Append share folder to cmake paths<commit_after>pub mod msg;
pub mod helpers;
pub mod error;
pub mod genmsg;

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;

#[macro_export]
macro_rules! rosmsg_main {
    ($($msg:expr),*)=> {
        fn main() {
            $crate::build_tools::depend_on_messages(&[
            $(
                $msg,
            )*
            ]);
        }
    }
}

#[macro_export]
macro_rules! rosmsg_include {
    () => {include!(concat!(env!("OUT_DIR"), "/msg.rs"));}
}

pub fn depend_on_messages(messages: &[&str]) {
    let cmake_paths = env::var("CMAKE_PREFIX_PATH")
        .unwrap_or(String::new())
        .split(":")
        .filter_map(append_share_folder)
        .collect::<Vec<String>>();
    let extra_paths = env::var("ROSRUST_MSG_PATH")
        .unwrap_or(String::new())
        .split(":")
        .map(String::from)
        .collect::<Vec<String>>();
    let paths = cmake_paths
        .iter()
        .chain(extra_paths.iter())
        .map(|v| v.as_str())
        .collect::<Vec<&str>>();
    let output = genmsg::depend_on_messages(paths.as_slice(), messages).unwrap();
    let out_dir = env::var("OUT_DIR").unwrap();
    let dest_path = Path::new(&out_dir).join("msg.rs");
    let mut f = File::create(&dest_path).unwrap();
    write!(f, "{}", output).unwrap();
}

fn append_share_folder(path: &str) -> Option<String> {
    Path::new(path)
        .join("share")
        .to_str()
        .map(|v| v.to_owned())
}
<|endoftext|>


<commit_before>//! Types related to topic metadata for introspection by clients.
//! Example: `KafkaClient::topics()`.

use std::collections::hash_map;
use std::fmt;

use super::KafkaClient;
use super::state::{ClientState, TopicPartitions, TopicPartitionIter, TopicPartition};

// public re-export
pub use super::state::Broker;
pub use super::state::TopicNames;

/// A view on the loaded metadata about topics and their partitions.
pub struct Topics<'a> {
    state: &'a ClientState,
}

impl<'a> Topics<'a> {
    /// Constructs a view of the currently loaded topic metadata from
    /// the specified kafka client.
    #[inline]
    pub fn new(client: &KafkaClient) -> Topics {
        Topics { state: &client.state }
    }

    /// Retrieves the number of the underlying topics.
    #[inline]
    pub fn len(&self) -> usize {
        self.state.num_topics()
    }

    /// Provides an iterator over the underlying topics.
    #[inline]
    pub fn iter(&'a self) -> TopicIter<'a> {
        TopicIter::new(self.state)
    }

    /// A conveniece method to return an iterator over the topics'
    /// names.
    #[inline]
    pub fn names(&'a self) -> TopicNames<'a> {
        self.state.topic_names()
    }

    /// A convenience method to determine whether the specified topic
    /// is known.
    #[inline]
    pub fn contains(&'a self, topic: &str) -> bool {
        self.state.contains_topic(topic)
    }

    /// Retrieves the partitions of a specified topic.
    #[inline]
    pub fn partitions(&'a self, topic: &str) -> Option<Partitions<'a>> {
        self.state.partitions_for(topic).map(|tp| {
            Partitions {
                state: self.state,
                tp: tp,
            }
        })
    }
}

impl<'a> fmt::Debug for Topics<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "Topics {{ topics: ["));
        let mut ts = self.iter();
        if let Some(t) = ts.next() {
            try!(write!(f, "{:?}", t));
        }
        for t in ts {
            try!(write!(f, ", {:?}", t));
        }
        write!(f, "] }}")
    }
}

impl<'a> IntoIterator for &'a Topics<'a> {
    type Item=Topic<'a>;
    type IntoIter=TopicIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

impl<'a> IntoIterator for Topics<'a> {
    type Item=Topic<'a>;
    type IntoIter=TopicIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        TopicIter::new(self.state)
    }
}

/// An interator over topics.
pub struct TopicIter<'a> {
    state: &'a ClientState,
    iter: hash_map::Iter<'a, String, TopicPartitions>,
}

impl<'a> TopicIter<'a> {
    fn new(state: &'a ClientState) -> TopicIter<'a> {
        TopicIter {
            state: state,
            iter: state.topic_partitions().iter(),
        }
    }
}

impl<'a> Iterator for TopicIter<'a> {
    type Item=Topic<'a>;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|(name, tps)| {
            Topic {
                state: self.state,
                name: &name[..],
                tp: tps,
            }
        })
    }
}

/// A view on the loaded metadata for a particular topic.
pub struct Topic<'a> {
    state: &'a ClientState,
    name: &'a str,
    tp: &'a TopicPartitions,
}

impl<'a> Topic<'a> {
    /// Retrieves the name of this topic.
    #[inline]
    pub fn name(&self) -> &str {
        self.name
    }

    /// Retrieves the list of all partitions for this topic.
    #[inline]
    pub fn partitions(&self) -> Partitions<'a> {
        Partitions {
            state: self.state,
            tp: self.tp,
        }
    }
}

impl<'a> fmt::Debug for Topic<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "Topic {{ name: {}, partitions: {:?} }}",
               self.name,
               self.partitions())
    }
}

/// Metadata relevant to partitions of a particular topic.
pub struct Partitions<'a> {
    state: &'a ClientState,
    tp: &'a TopicPartitions,
}

impl<'a> Partitions<'a> {
    /// Retrieves the number of the topic's partitions.
    #[inline]
    pub fn len(&self) -> usize {
        self.tp.len()
    }

    /// Tests for `.len() > 0`.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.tp.is_empty()
    }

    /// Retrieves an iterator of the partitions of the underlying topic.
    #[inline]
    pub fn iter(&self) -> PartitionIter<'a> {
        PartitionIter::new(self.state, self.tp)
    }

    /// Finds a specified partition identified by its id.
    #[inline]
    pub fn partition(&self, partition_id: i32) -> Option<Partition<'a>> {
        self.tp.partition(partition_id).map(|p| Partition::new(self.state, p, partition_id))
    }

    /// Convenience method to retrieve the identifiers of all
    /// currently "available" partitions.  Such partitions are known
    /// to have a leader broker and can be sent messages to.
    #[inline]
    pub fn available_ids(&self) -> Vec<i32> {
        self.tp
            .iter()
            .filter_map(|(id, p)| p.broker(self.state).map(|_| id))
            .collect()
    }
}

impl<'a> fmt::Debug for Partitions<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "Partitions {{ ["));
        let mut ps = self.iter();
        if let Some(p) = ps.next() {
            try!(write!(f, "{:?}", p));
        }
        for p in ps {
            try!(write!(f, ", {:?}", p));
        }
        write!(f, "] }}")
    }
}

impl<'a> IntoIterator for &'a Partitions<'a> {
    type Item=Partition<'a>;
    type IntoIter=PartitionIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

impl<'a> IntoIterator for Partitions<'a> {
    type Item=Partition<'a>;
    type IntoIter=PartitionIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        PartitionIter::new(self.state, self.tp)
    }
}

/// An interator over a topic's partitions.
pub struct PartitionIter<'a> {
    state: &'a ClientState,
    iter: TopicPartitionIter<'a>,
}

impl<'a> PartitionIter<'a> {
    fn new(state: &'a ClientState, tp: &'a TopicPartitions) -> Self {
        PartitionIter {
            state: state,
            iter: tp.iter(),
        }
    }
}

impl<'a> Iterator for PartitionIter<'a> {
    type Item=Partition<'a>;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|(id, p)| Partition::new(self.state, p, id))
    }
}

/// Metadata about a particular topic partition.
///
/// A partition can be seen as either available or not by
/// `kafka-rust`.  "Available" partitions are partitions with an
/// assigned leader broker and can be send messages to or fetched
/// messages from.  Non-available partitions are ignored by
/// `kafka-rust`.  Whether or not a partition is currently "available"
/// can be determined by testing for `partition.leader().is_some()` or
/// more directly through `partition.is_available()`.
pub struct Partition<'a> {
    state: &'a ClientState,
    partition: &'a TopicPartition,
    id: i32,
}

impl<'a> Partition<'a> {
    fn new(state: &'a ClientState, partition: &'a TopicPartition, id: i32) -> Partition<'a> {
        Partition {
            state: state,
            partition: partition,
            id: id,
        }
    }

    /// Retrieves the identifier of this topic partition.
    #[inline]
    pub fn id(&self) -> i32 {
        self.id
    }

    /// Retrieves the current leader broker of this partition - if
    /// any.  A partition with a leader is said to be "available".
    #[inline]
    pub fn leader(&self) -> Option<&Broker> {
        self.partition.broker(self.state)
    }

    /// Determines whether this partition is currently "available".
    /// See `Partition::leader()`.
    pub fn is_available(&self) -> bool {
        self.leader().is_some()
    }
}

impl<'a> fmt::Debug for Partition<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "Partition {{ id: {}, leader: {:?} }}",
               self.id(),
               self.leader())
    }
}
<commit_msg>Widen lifetime<commit_after>//! Types related to topic metadata for introspection by clients.
//! Example: `KafkaClient::topics()`.

use std::collections::hash_map;
use std::fmt;

use super::KafkaClient;
use super::state::{ClientState, TopicPartitions, TopicPartitionIter, TopicPartition};

// public re-export
pub use super::state::Broker;
pub use super::state::TopicNames;

/// A view on the loaded metadata about topics and their partitions.
pub struct Topics<'a> {
    state: &'a ClientState,
}

impl<'a> Topics<'a> {
    /// Constructs a view of the currently loaded topic metadata from
    /// the specified kafka client.
    #[inline]
    pub fn new(client: &KafkaClient) -> Topics {
        Topics { state: &client.state }
    }

    /// Retrieves the number of the underlying topics.
    #[inline]
    pub fn len(&self) -> usize {
        self.state.num_topics()
    }

    /// Provides an iterator over the underlying topics.
    #[inline]
    pub fn iter(&'a self) -> TopicIter<'a> {
        TopicIter::new(self.state)
    }

    /// A conveniece method to return an iterator over the topics'
    /// names.
    #[inline]
    pub fn names(&'a self) -> TopicNames<'a> {
        self.state.topic_names()
    }

    /// A convenience method to determine whether the specified topic
    /// is known.
    #[inline]
    pub fn contains(&'a self, topic: &str) -> bool {
        self.state.contains_topic(topic)
    }

    /// Retrieves the partitions of a specified topic.
    #[inline]
    pub fn partitions(&'a self, topic: &str) -> Option<Partitions<'a>> {
        self.state.partitions_for(topic).map(|tp| {
            Partitions {
                state: self.state,
                tp: tp,
            }
        })
    }
}

impl<'a> fmt::Debug for Topics<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "Topics {{ topics: ["));
        let mut ts = self.iter();
        if let Some(t) = ts.next() {
            try!(write!(f, "{:?}", t));
        }
        for t in ts {
            try!(write!(f, ", {:?}", t));
        }
        write!(f, "] }}")
    }
}

impl<'a> IntoIterator for &'a Topics<'a> {
    type Item=Topic<'a>;
    type IntoIter=TopicIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

impl<'a> IntoIterator for Topics<'a> {
    type Item=Topic<'a>;
    type IntoIter=TopicIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        TopicIter::new(self.state)
    }
}

/// An interator over topics.
pub struct TopicIter<'a> {
    state: &'a ClientState,
    iter: hash_map::Iter<'a, String, TopicPartitions>,
}

impl<'a> TopicIter<'a> {
    fn new(state: &'a ClientState) -> TopicIter<'a> {
        TopicIter {
            state: state,
            iter: state.topic_partitions().iter(),
        }
    }
}

impl<'a> Iterator for TopicIter<'a> {
    type Item=Topic<'a>;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|(name, tps)| {
            Topic {
                state: self.state,
                name: &name[..],
                tp: tps,
            }
        })
    }
}

/// A view on the loaded metadata for a particular topic.
pub struct Topic<'a> {
    state: &'a ClientState,
    name: &'a str,
    tp: &'a TopicPartitions,
}

impl<'a> Topic<'a> {
    /// Retrieves the name of this topic.
    #[inline]
    pub fn name(&self) -> &str {
        self.name
    }

    /// Retrieves the list of all partitions for this topic.
    #[inline]
    pub fn partitions(&self) -> Partitions<'a> {
        Partitions {
            state: self.state,
            tp: self.tp,
        }
    }
}

impl<'a> fmt::Debug for Topic<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "Topic {{ name: {}, partitions: {:?} }}",
               self.name,
               self.partitions())
    }
}

/// Metadata relevant to partitions of a particular topic.
pub struct Partitions<'a> {
    state: &'a ClientState,
    tp: &'a TopicPartitions,
}

impl<'a> Partitions<'a> {
    /// Retrieves the number of the topic's partitions.
    #[inline]
    pub fn len(&self) -> usize {
        self.tp.len()
    }

    /// Tests for `.len() > 0`.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.tp.is_empty()
    }

    /// Retrieves an iterator of the partitions of the underlying topic.
    #[inline]
    pub fn iter(&self) -> PartitionIter<'a> {
        PartitionIter::new(self.state, self.tp)
    }

    /// Finds a specified partition identified by its id.
    #[inline]
    pub fn partition(&self, partition_id: i32) -> Option<Partition<'a>> {
        self.tp.partition(partition_id).map(|p| Partition::new(self.state, p, partition_id))
    }

    /// Convenience method to retrieve the identifiers of all
    /// currently "available" partitions.  Such partitions are known
    /// to have a leader broker and can be sent messages to.
    #[inline]
    pub fn available_ids(&self) -> Vec<i32> {
        self.tp
            .iter()
            .filter_map(|(id, p)| p.broker(self.state).map(|_| id))
            .collect()
    }
}

impl<'a> fmt::Debug for Partitions<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "Partitions {{ ["));
        let mut ps = self.iter();
        if let Some(p) = ps.next() {
            try!(write!(f, "{:?}", p));
        }
        for p in ps {
            try!(write!(f, ", {:?}", p));
        }
        write!(f, "] }}")
    }
}

impl<'a> IntoIterator for &'a Partitions<'a> {
    type Item=Partition<'a>;
    type IntoIter=PartitionIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

impl<'a> IntoIterator for Partitions<'a> {
    type Item=Partition<'a>;
    type IntoIter=PartitionIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        PartitionIter::new(self.state, self.tp)
    }
}

/// An interator over a topic's partitions.
pub struct PartitionIter<'a> {
    state: &'a ClientState,
    iter: TopicPartitionIter<'a>,
}

impl<'a> PartitionIter<'a> {
    fn new(state: &'a ClientState, tp: &'a TopicPartitions) -> Self {
        PartitionIter {
            state: state,
            iter: tp.iter(),
        }
    }
}

impl<'a> Iterator for PartitionIter<'a> {
    type Item=Partition<'a>;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|(id, p)| Partition::new(self.state, p, id))
    }
}

/// Metadata about a particular topic partition.
///
/// A partition can be seen as either available or not by
/// `kafka-rust`.  "Available" partitions are partitions with an
/// assigned leader broker and can be send messages to or fetched
/// messages from.  Non-available partitions are ignored by
/// `kafka-rust`.  Whether or not a partition is currently "available"
/// can be determined by testing for `partition.leader().is_some()` or
/// more directly through `partition.is_available()`.
pub struct Partition<'a> {
    state: &'a ClientState,
    partition: &'a TopicPartition,
    id: i32,
}

impl<'a> Partition<'a> {
    fn new(state: &'a ClientState, partition: &'a TopicPartition, id: i32) -> Partition<'a> {
        Partition {
            state: state,
            partition: partition,
            id: id,
        }
    }

    /// Retrieves the identifier of this topic partition.
    #[inline]
    pub fn id(&self) -> i32 {
        self.id
    }

    /// Retrieves the current leader broker of this partition - if
    /// any.  A partition with a leader is said to be "available".
    #[inline]
    pub fn leader(&self) -> Option<&'a Broker> {
        self.partition.broker(self.state)
    }

    /// Determines whether this partition is currently "available".
    /// See `Partition::leader()`.
    pub fn is_available(&self) -> bool {
        self.leader().is_some()
    }
}

impl<'a> fmt::Debug for Partition<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "Partition {{ id: {}, leader: {:?} }}",
               self.id(),
               self.leader())
    }
}
<|endoftext|>


<commit_before>use std::io::{Result, Write};

use analysis;
use analysis::namespaces::MAIN;
use env::Env;
use file_saver::save_to_file;
use library::*;
use library::Type as LType;
use writer::primitives;

use stripper_interface as stripper;
use stripper_interface::Type as SType;
use stripper_interface::TypeStruct;
use stripper_interface::{FILE, /*FILE_COMMENT, */MOD_COMMENT};

mod format;

trait FunctionTraitType {
    fn functions(&self) -> &Vec<Function>;
    fn name(&self) -> &str;
}

macro_rules! impl_function_trait_type {
    ($ty:ident) => {
        impl FunctionTraitType for $ty {
            fn functions(&self) -> &Vec<Function> {
                &self.functions
            }

            fn name(&self) -> &str {
                &self.name
            }
        }
    }
}

impl_function_trait_type!(Class);
impl_function_trait_type!(Interface);

trait ToStripperType {
    fn convert(&self) -> stripper::TypeStruct;
    fn doc(&self) -> Option<String>;
    fn doc_deprecated(&self) -> Option<String>;
}

macro_rules! impl_to_stripper_type {
    ($ty:ident, $enum_var:ident) => {
        impl ToStripperType for $ty {
            fn convert(&self) -> TypeStruct {
                TypeStruct::new(SType::$enum_var, &self.name)
            }

            fn doc(&self) -> Option<String> {
                self.doc.clone()
            }

            fn doc_deprecated(&self) -> Option<String> {
                self.doc_deprecated.clone()
            }
        }
    }
}

impl_to_stripper_type!(Alias, Struct);
impl_to_stripper_type!(Constant, Const);
impl_to_stripper_type!(Member, Variant);
impl_to_stripper_type!(Enumeration, Enum);
impl_to_stripper_type!(Bitfield, Type);
impl_to_stripper_type!(Record, Type);
impl_to_stripper_type!(Field, Variant);
impl_to_stripper_type!(Union, Struct);
impl_to_stripper_type!(Function, Fn);
impl_to_stripper_type!(Interface, Trait);
impl_to_stripper_type!(Class, Struct);
impl_to_stripper_type!(Namespace, Mod);
impl_to_stripper_type!(Parameter, Variant);

pub fn generate(env: &Env) {
    let path =  env.config.target_path.join("doc.cmts");
    println!("Generating documentation {:?}", path);
    save_to_file(&path, env.config.make_backup,
        |w| generate_doc(w, &env));
}

fn generate_doc(mut w: &mut Write, env: &Env) -> Result<()> {
    try!(writeln!(w, "{}*", FILE));

    let namespace = env.library.namespace(MAIN);
    for obj in env.config.objects.values() {
        if !obj.status.need_generate() {
            continue;
        }

        let info = analysis::object::class(env, obj)
            .or_else(|| analysis::object::interface(env, obj));
        let class_analysis = match info {
            Some(info) => info,
            None => continue,
        };
        let has_trait = class_analysis.has_children;

        try!(handle_type(w, &env.library.type_(class_analysis.type_id), None, has_trait,
                         true, &env));
    }

    for ty in namespace.types.iter().filter_map(|t| t.as_ref()) {
        try!(handle_type(&mut w, &ty, None, false, false, &env));
    }
    Ok(())
}

fn handle_type(w: &mut Write, ty: &LType, parent: Option<Box<TypeStruct>>,
               has_trait: bool, handle_structs: bool,
               env: &Env) -> Result<()> {
    match *ty {
        LType::Alias(ref a) => create_sub_doc(w, a, parent, env),
        LType::Enumeration(ref e) => create_enum_doc(w, &e, parent, env),
        LType::Function(ref f) => create_fn_doc(w, &f, parent, env),
        LType::Interface(ref i) if handle_structs => create_class_doc(w, i, parent, has_trait,
                                                                      env),
        LType::Class(ref c) if handle_structs => create_class_doc(w, c, parent, has_trait,
                                                                  env),
        _ => Ok(()),
    }
}

fn create_class_doc<T: FunctionTraitType + ToStripperType>(w: &mut Write, class: &T,
                                                           parent: Option<Box<TypeStruct>>,
                                                           has_trait: bool,
                                                           env: &Env)
                                                          -> Result<()> {
    let tabs : String = primitives::tabs(compute_indent(&parent));
    let ty = TypeStruct { parent: parent, ..class.convert() };
    let ty_ext = TypeStruct {
        ty: SType::Trait,
        name: format!("{}Ext", ty.name),
        ..ty.clone()
    };

    //try!(writeln!(w, "{}src/auto/{}.rs", FILE, module_name(&class.name())));
    if class.doc().is_some() || class.doc_deprecated().is_some() {
        try!(writeln!(w, "{}{}", MOD_COMMENT, ty));
    }

    if let Some(ref class_doc) = class.doc() {
        try!(write_lines(w, &class_doc, &tabs, env));
    }
    if let Some(ref class_doc) = class.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &class_doc, &tabs, env));
    }

    let ty = TypeStruct { ty: SType::Impl, ..ty };

    for function in class.functions().iter() {
        let ty = if has_trait && function.parameters.iter().any(|p| p.instance_parameter) {
            ty_ext.clone()
        }
        else {
            ty.clone()
        };
        try!(create_fn_doc(w, &function, Some(Box::new(ty)), env));
    }
    Ok(())
}

fn create_enum_doc(w: &mut Write, enum_: &Enumeration,
                   parent: Option<Box<TypeStruct>>,
                   env: &Env) -> Result<()> {
    let indent = compute_indent(&parent);
    let mut ty = enum_.convert();
    ty.parent = parent;
    let tabs : String = primitives::tabs(indent);

    if enum_.doc().is_some() || enum_.doc_deprecated().is_some() {
        try!(writeln!(w, "{}{}", MOD_COMMENT, ty));
    }

    if let Some(ref enum_doc) = enum_.doc() {
        try!(write_lines(w, &enum_doc, &tabs, env));
    }
    if let Some(ref enum_doc) = enum_.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &enum_doc, &tabs, env));
    }

    let tabs : String = primitives::tabs(indent + 1);
    for member_doc in enum_.members.iter() {
        let mut sub_ty : TypeStruct = member_doc.convert();

        if member_doc.doc().is_some() || member_doc.doc_deprecated().is_some() {
            sub_ty.parent = Some(Box::new(ty.clone()));
            try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));
        }
        if let Some(ref m_doc) = member_doc.doc() {
            try!(write_lines(w, &m_doc, &tabs, env));
        }
        if let Some(ref m_doc) = member_doc.doc_deprecated() {
            try!(write_header(w, &tabs, "Deprecated"));
            try!(write_lines(w, &m_doc, &tabs, env));
        }
    }
    Ok(())
}

fn create_fn_doc(w: &mut Write, fn_: &Function, parent: Option<Box<TypeStruct>>,
                 env: &Env) -> Result<()> {
    let tabs : String = primitives::tabs(compute_indent(&parent) + 1);

    if fn_.doc().is_none() && fn_.doc_deprecated().is_none() && fn_.ret.doc().is_none() {
        if fn_.parameters.iter().all(|x| {
            x.doc().is_none()
        }) {
            return Ok(());
        }
    }
    let mut sub_ty = fn_.convert();
    sub_ty.parent = parent;
    try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));

    if let Some(ref docs) = fn_.doc() {
        try!(write_lines(w, &docs, &tabs, env));
    };
    if let Some(ref docs) = fn_.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &docs, &tabs, env));
    };

    if fn_.parameters.iter().any(|x| {
           x.instance_parameter == false && !x.name.is_empty() && x.doc().is_some()
       }) {
        try!(write_header(w, &tabs, "Parameters"));
    }
    for parameter in fn_.parameters.iter() {
        if parameter.instance_parameter || parameter.name.is_empty() {
            continue
        }
        if let Some(ref parameter_doc) = parameter.doc() {
            try!(writeln!(w, "{}/// ## {}:", tabs, parameter.name));
            try!(write_lines(w, &parameter_doc, &tabs, env));
        }
    }

    if let Some(ref doc) = fn_.ret.doc() {
        try!(write_header(w, &tabs, "Returns"));
        try!(write_lines(w, &doc, &tabs, env));
    }
    Ok(())
}

fn write_lines(w: &mut Write, lines: &str, tabs: &str,
               env: &Env) -> Result<()> {
    for line in format::reformat_doc(&lines, env).split("\n") {
        try!(writeln!(w, "{}/// {}", tabs, line));
    }
    Ok(())
}

fn create_sub_doc<T: ToStripperType>(w: &mut Write, ty: &T,
                                     parent: Option<Box<TypeStruct>>,
                                     env: &Env) -> Result<()> {
    let tabs : String = primitives::tabs(compute_indent(&parent));
    let mut sub_ty = ty.convert();
    sub_ty.parent = parent;

    if ty.doc().is_some() || ty.doc_deprecated().is_some() {
        try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));
    }

    if let Some(doc) = ty.doc() {
        try!(write_lines(w, &doc, &tabs, env));
    }
    if let Some(doc) = ty.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &doc, &tabs, env));
    }
    Ok(())
}

fn compute_indent(e: &Option<Box<TypeStruct>>) -> usize {
    match *e {
        Some(ref e) if e.parent.is_some() => compute_indent(&e.parent) + 1,
        _ => 0,
    }
}

fn write_header(w: &mut Write, tabs: &str, header: &str) -> Result<()> {
    writeln!(w, "{0}///\n{0}/// # {1}\n{0}///", tabs, header)
}
<commit_msg>Simplify signatures, drop redundant `parent` parameters<commit_after>use std::io::{Result, Write};

use analysis;
use analysis::namespaces::MAIN;
use env::Env;
use file_saver::save_to_file;
use library::*;
use library::Type as LType;
use writer::primitives;

use stripper_interface as stripper;
use stripper_interface::Type as SType;
use stripper_interface::TypeStruct;
use stripper_interface::{FILE, /*FILE_COMMENT, */MOD_COMMENT};

mod format;

trait FunctionTraitType {
    fn functions(&self) -> &Vec<Function>;
    fn name(&self) -> &str;
}

macro_rules! impl_function_trait_type {
    ($ty:ident) => {
        impl FunctionTraitType for $ty {
            fn functions(&self) -> &Vec<Function> {
                &self.functions
            }

            fn name(&self) -> &str {
                &self.name
            }
        }
    }
}

impl_function_trait_type!(Class);
impl_function_trait_type!(Interface);

trait ToStripperType {
    fn convert(&self) -> stripper::TypeStruct;
    fn doc(&self) -> Option<String>;
    fn doc_deprecated(&self) -> Option<String>;
}

macro_rules! impl_to_stripper_type {
    ($ty:ident, $enum_var:ident) => {
        impl ToStripperType for $ty {
            fn convert(&self) -> TypeStruct {
                TypeStruct::new(SType::$enum_var, &self.name)
            }

            fn doc(&self) -> Option<String> {
                self.doc.clone()
            }

            fn doc_deprecated(&self) -> Option<String> {
                self.doc_deprecated.clone()
            }
        }
    }
}

impl_to_stripper_type!(Alias, Struct);
impl_to_stripper_type!(Constant, Const);
impl_to_stripper_type!(Member, Variant);
impl_to_stripper_type!(Enumeration, Enum);
impl_to_stripper_type!(Bitfield, Type);
impl_to_stripper_type!(Record, Type);
impl_to_stripper_type!(Field, Variant);
impl_to_stripper_type!(Union, Struct);
impl_to_stripper_type!(Function, Fn);
impl_to_stripper_type!(Interface, Trait);
impl_to_stripper_type!(Class, Struct);
impl_to_stripper_type!(Namespace, Mod);
impl_to_stripper_type!(Parameter, Variant);

pub fn generate(env: &Env) {
    let path =  env.config.target_path.join("doc.cmts");
    println!("Generating documentation {:?}", path);
    save_to_file(&path, env.config.make_backup,
        |w| generate_doc(w, &env));
}

fn generate_doc(mut w: &mut Write, env: &Env) -> Result<()> {
    try!(writeln!(w, "{}*", FILE));

    let namespace = env.library.namespace(MAIN);
    for obj in env.config.objects.values() {
        if !obj.status.need_generate() {
            continue;
        }

        let info = analysis::object::class(env, obj)
            .or_else(|| analysis::object::interface(env, obj));
        if let Some(info) = info {
            try!(create_object_doc(w, env, &info));
        }
    }

    for ty in namespace.types.iter().filter_map(|t| t.as_ref()) {
        try!(handle_type(&mut w, &ty, &env));
    }
    Ok(())
}

fn handle_type(w: &mut Write, ty: &LType, env: &Env) -> Result<()> {
    match *ty {
        LType::Alias(ref a) => create_sub_doc(w, a, env),
        LType::Enumeration(ref e) => create_enum_doc(w, &e, env),
        LType::Function(ref f) => create_fn_doc(w, &f, None, env),
        _ => Ok(()),
    }
}

fn create_class_doc<T: FunctionTraitType + ToStripperType>(w: &mut Write, class: &T,
                                                           parent: Option<Box<TypeStruct>>,
                                                           has_trait: bool,
                                                           env: &Env)
                                                          -> Result<()> {
    let tabs : String = primitives::tabs(compute_indent(&parent));
    let ty = TypeStruct { parent: parent, ..class.convert() };
    let ty_ext = TypeStruct {
        ty: SType::Trait,
        name: format!("{}Ext", ty.name),
        ..ty.clone()
    };

    //try!(writeln!(w, "{}src/auto/{}.rs", FILE, module_name(&class.name())));
    if class.doc().is_some() || class.doc_deprecated().is_some() {
        try!(writeln!(w, "{}{}", MOD_COMMENT, ty));
    }

    if let Some(ref class_doc) = class.doc() {
        try!(write_lines(w, &class_doc, &tabs, env));
    }
    if let Some(ref class_doc) = class.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &class_doc, &tabs, env));
    }

    let ty = TypeStruct { ty: SType::Impl, ..ty };

    for function in class.functions().iter() {
        let ty = if has_trait && function.parameters.iter().any(|p| p.instance_parameter) {
            ty_ext.clone()
        }
        else {
            ty.clone()
        };
        try!(create_fn_doc(w, &function, Some(Box::new(ty)), env));
    }
    Ok(())
}

fn create_enum_doc(w: &mut Write, enum_: &Enumeration, env: &Env) -> Result<()> {
    let ty = enum_.convert();
    let tabs = "";

    if enum_.doc().is_some() || enum_.doc_deprecated().is_some() {
        try!(writeln!(w, "{}{}", MOD_COMMENT, ty));
    }

    if let Some(ref enum_doc) = enum_.doc() {
        try!(write_lines(w, &enum_doc, &tabs, env));
    }
    if let Some(ref enum_doc) = enum_.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &enum_doc, &tabs, env));
    }

    let tabs = "\t";
    for member_doc in enum_.members.iter() {
        let mut sub_ty : TypeStruct = member_doc.convert();

        if member_doc.doc().is_some() || member_doc.doc_deprecated().is_some() {
            sub_ty.parent = Some(Box::new(ty.clone()));
            try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));
        }
        if let Some(ref m_doc) = member_doc.doc() {
            try!(write_lines(w, &m_doc, &tabs, env));
        }
        if let Some(ref m_doc) = member_doc.doc_deprecated() {
            try!(write_header(w, &tabs, "Deprecated"));
            try!(write_lines(w, &m_doc, &tabs, env));
        }
    }
    Ok(())
}

fn create_fn_doc(w: &mut Write, fn_: &Function, parent: Option<Box<TypeStruct>>,
                 env: &Env) -> Result<()> {
    let tabs : String = primitives::tabs(compute_indent(&parent) + 1);

    if fn_.doc().is_none() && fn_.doc_deprecated().is_none() && fn_.ret.doc().is_none() {
        if fn_.parameters.iter().all(|x| {
            x.doc().is_none()
        }) {
            return Ok(());
        }
    }
    let mut sub_ty = fn_.convert();
    sub_ty.parent = parent;
    try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));

    if let Some(ref docs) = fn_.doc() {
        try!(write_lines(w, &docs, &tabs, env));
    };
    if let Some(ref docs) = fn_.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &docs, &tabs, env));
    };

    if fn_.parameters.iter().any(|x| {
           x.instance_parameter == false && !x.name.is_empty() && x.doc().is_some()
       }) {
        try!(write_header(w, &tabs, "Parameters"));
    }
    for parameter in fn_.parameters.iter() {
        if parameter.instance_parameter || parameter.name.is_empty() {
            continue
        }
        if let Some(ref parameter_doc) = parameter.doc() {
            try!(writeln!(w, "{}/// ## {}:", tabs, parameter.name));
            try!(write_lines(w, &parameter_doc, &tabs, env));
        }
    }

    if let Some(ref doc) = fn_.ret.doc() {
        try!(write_header(w, &tabs, "Returns"));
        try!(write_lines(w, &doc, &tabs, env));
    }
    Ok(())
}

fn write_lines(w: &mut Write, lines: &str, tabs: &str,
               env: &Env) -> Result<()> {
    for line in format::reformat_doc(&lines, env).split("\n") {
        try!(writeln!(w, "{}/// {}", tabs, line));
    }
    Ok(())
}

fn create_sub_doc<T: ToStripperType>(w: &mut Write, ty: &T, env: &Env) -> Result<()> {
    let tabs = "";
    let sub_ty = ty.convert();

    if ty.doc().is_some() || ty.doc_deprecated().is_some() {
        try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));
    }

    if let Some(doc) = ty.doc() {
        try!(write_lines(w, &doc, &tabs, env));
    }
    if let Some(doc) = ty.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &doc, &tabs, env));
    }
    Ok(())
}

fn compute_indent(e: &Option<Box<TypeStruct>>) -> usize {
    match *e {
        Some(ref e) if e.parent.is_some() => compute_indent(&e.parent) + 1,
        _ => 0,
    }
}

fn write_header(w: &mut Write, tabs: &str, header: &str) -> Result<()> {
    writeln!(w, "{0}///\n{0}/// # {1}\n{0}///", tabs, header)
}
<|endoftext|>


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

//! The compiler code necessary for `#[derive(Decodable)]`. See encodable.rs for more.

use ast;
use ast::{MetaItem, Item, Expr, MutMutable};
use codemap::Span;
use ext::base::ExtCtxt;
use ext::build::AstBuilder;
use ext::deriving::generic::*;
use ext::deriving::generic::ty::*;
use parse::token::InternedString;
use parse::token;
use ptr::P;

pub fn expand_deriving_rustc_decodable<F>(cx: &mut ExtCtxt,
                                          span: Span,
                                          mitem: &MetaItem,
                                          item: &Item,
                                          push: F) where
    F: FnOnce(P<Item>),
{
    expand_deriving_decodable_imp(cx, span, mitem, item, push, "rustc_serialize")
}

pub fn expand_deriving_decodable<F>(cx: &mut ExtCtxt,
                                    span: Span,
                                    mitem: &MetaItem,
                                    item: &Item,
                                    push: F) where
    F: FnOnce(P<Item>),
{
    expand_deriving_decodable_imp(cx, span, mitem, item, push, "serialize")
}

fn expand_deriving_decodable_imp<F>(cx: &mut ExtCtxt,
                                    span: Span,
                                    mitem: &MetaItem,
                                    item: &Item,
                                    push: F,
                                    krate: &'static str) where
    F: FnOnce(P<Item>),
{
    let trait_def = TraitDef {
        span: span,
        attributes: Vec::new(),
        path: Path::new_(vec!(krate, "Decodable"), None, vec!(), true),
        additional_bounds: Vec::new(),
        generics: LifetimeBounds::empty(),
        methods: vec!(
            MethodDef {
                name: "decode",
                generics: LifetimeBounds {
                    lifetimes: Vec::new(),
                    bounds: vec!(("__D", vec!(Path::new_(
                                    vec!(krate, "Decoder"), None,
                                    vec!(), true))))
                },
                explicit_self: None,
                args: vec!(Ptr(box Literal(Path::new_local("__D")),
                            Borrowed(None, MutMutable))),
                ret_ty: Literal(Path::new_(
                    vec!("std", "result", "Result"),
                    None,
                    vec!(box Self, box Literal(Path::new_(
                        vec!["__D", "Error"], None, vec![], false
                    ))),
                    true
                )),
                attributes: Vec::new(),
                combine_substructure: combine_substructure(box |a, b, c| {
                    decodable_substructure(a, b, c, krate)
                }),
            })
    };

    trait_def.expand(cx, mitem, item, push)
}

fn decodable_substructure(cx: &mut ExtCtxt, trait_span: Span,
                          substr: &Substructure,
                          krate: &str) -> P<Expr> {
    let decoder = substr.nonself_args[0].clone();
    let recurse = vec!(cx.ident_of(krate),
                    cx.ident_of("Decodable"),
                    cx.ident_of("decode"));
    // throw an underscore in front to suppress unused variable warnings
    let blkarg = cx.ident_of("_d");
    let blkdecoder = cx.expr_ident(trait_span, blkarg);
    let calldecode = cx.expr_call_global(trait_span, recurse, vec!(blkdecoder.clone()));
    let lambdadecode = cx.lambda_expr_1(trait_span, calldecode, blkarg);

    return match *substr.fields {
        StaticStruct(_, ref summary) => {
            let nfields = match *summary {
                Unnamed(ref fields) => fields.len(),
                Named(ref fields) => fields.len()
            };
            let read_struct_field = cx.ident_of("read_struct_field");

            let path = cx.path_ident(trait_span, substr.type_ident);
            let result = decode_static_fields(cx,
                                              trait_span,
                                              path,
                                              summary,
                                              |cx, span, name, field| {
                cx.expr_try(span,
                    cx.expr_method_call(span, blkdecoder.clone(), read_struct_field,
                                        vec!(cx.expr_str(span, name),
                                          cx.expr_uint(span, field),
                                          lambdadecode.clone())))
            });
            let result = cx.expr_ok(trait_span, result);
            cx.expr_method_call(trait_span,
                                decoder,
                                cx.ident_of("read_struct"),
                                vec!(
                cx.expr_str(trait_span, token::get_ident(substr.type_ident)),
                cx.expr_uint(trait_span, nfields),
                cx.lambda_expr_1(trait_span, result, blkarg)
            ))
        }
        StaticEnum(_, ref fields) => {
            let variant = cx.ident_of("i");

            let mut arms = Vec::new();
            let mut variants = Vec::new();
            let rvariant_arg = cx.ident_of("read_enum_variant_arg");

            for (i, &(name, v_span, ref parts)) in fields.iter().enumerate() {
                variants.push(cx.expr_str(v_span, token::get_ident(name)));

                let path = cx.path(trait_span, vec![substr.type_ident, name]);
                let decoded = decode_static_fields(cx,
                                                   v_span,
                                                   path,
                                                   parts,
                                                   |cx, span, _, field| {
                    let idx = cx.expr_uint(span, field);
                    cx.expr_try(span,
                        cx.expr_method_call(span, blkdecoder.clone(), rvariant_arg,
                                            vec!(idx, lambdadecode.clone())))
                });

                arms.push(cx.arm(v_span,
                                 vec!(cx.pat_lit(v_span, cx.expr_uint(v_span, i))),
                                 decoded));
            }

            arms.push(cx.arm_unreachable(trait_span));

            let result = cx.expr_ok(trait_span,
                                    cx.expr_match(trait_span,
                                                  cx.expr_ident(trait_span, variant), arms));
            let lambda = cx.lambda_expr(trait_span, vec!(blkarg, variant), result);
            let variant_vec = cx.expr_vec(trait_span, variants);
            let variant_vec = cx.expr_addr_of(trait_span, variant_vec);
            let result = cx.expr_method_call(trait_span, blkdecoder,
                                             cx.ident_of("read_enum_variant"),
                                             vec!(variant_vec, lambda));
            cx.expr_method_call(trait_span,
                                decoder,
                                cx.ident_of("read_enum"),
                                vec!(
                cx.expr_str(trait_span, token::get_ident(substr.type_ident)),
                cx.lambda_expr_1(trait_span, result, blkarg)
            ))
        }
        _ => cx.bug("expected StaticEnum or StaticStruct in deriving(Decodable)")
    };
}

/// Create a decoder for a single enum variant/struct:
/// - `outer_pat_path` is the path to this enum variant/struct
/// - `getarg` should retrieve the `uint`-th field with name `@str`.
fn decode_static_fields<F>(cx: &mut ExtCtxt,
                           trait_span: Span,
                           outer_pat_path: ast::Path,
                           fields: &StaticFields,
                           mut getarg: F)
                           -> P<Expr> where
    F: FnMut(&mut ExtCtxt, Span, InternedString, uint) -> P<Expr>,
{
    match *fields {
        Unnamed(ref fields) => {
            let path_expr = cx.expr_path(outer_pat_path);
            if fields.is_empty() {
                path_expr
            } else {
                let fields = fields.iter().enumerate().map(|(i, &span)| {
                    getarg(cx, span,
                           token::intern_and_get_ident(&format!("_field{}", i)[]),
                           i)
                }).collect();

                cx.expr_call(trait_span, path_expr, fields)
            }
        }
        Named(ref fields) => {
            // use the field's span to get nicer error messages.
            let fields = fields.iter().enumerate().map(|(i, &(name, span))| {
                let arg = getarg(cx, span, token::get_ident(name), i);
                cx.field_imm(span, name, arg)
            }).collect();
            cx.expr_struct(trait_span, outer_pat_path, fields)
        }
    }
}
<commit_msg>rollup merge of #21199: dotdash/decode_lamba<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The compiler code necessary for `#[derive(Decodable)]`. See encodable.rs for more.

use ast;
use ast::{MetaItem, Item, Expr, MutMutable};
use codemap::Span;
use ext::base::ExtCtxt;
use ext::build::AstBuilder;
use ext::deriving::generic::*;
use ext::deriving::generic::ty::*;
use parse::token::InternedString;
use parse::token;
use ptr::P;

pub fn expand_deriving_rustc_decodable<F>(cx: &mut ExtCtxt,
                                          span: Span,
                                          mitem: &MetaItem,
                                          item: &Item,
                                          push: F) where
    F: FnOnce(P<Item>),
{
    expand_deriving_decodable_imp(cx, span, mitem, item, push, "rustc_serialize")
}

pub fn expand_deriving_decodable<F>(cx: &mut ExtCtxt,
                                    span: Span,
                                    mitem: &MetaItem,
                                    item: &Item,
                                    push: F) where
    F: FnOnce(P<Item>),
{
    expand_deriving_decodable_imp(cx, span, mitem, item, push, "serialize")
}

fn expand_deriving_decodable_imp<F>(cx: &mut ExtCtxt,
                                    span: Span,
                                    mitem: &MetaItem,
                                    item: &Item,
                                    push: F,
                                    krate: &'static str) where
    F: FnOnce(P<Item>),
{
    let trait_def = TraitDef {
        span: span,
        attributes: Vec::new(),
        path: Path::new_(vec!(krate, "Decodable"), None, vec!(), true),
        additional_bounds: Vec::new(),
        generics: LifetimeBounds::empty(),
        methods: vec!(
            MethodDef {
                name: "decode",
                generics: LifetimeBounds {
                    lifetimes: Vec::new(),
                    bounds: vec!(("__D", vec!(Path::new_(
                                    vec!(krate, "Decoder"), None,
                                    vec!(), true))))
                },
                explicit_self: None,
                args: vec!(Ptr(box Literal(Path::new_local("__D")),
                            Borrowed(None, MutMutable))),
                ret_ty: Literal(Path::new_(
                    vec!("std", "result", "Result"),
                    None,
                    vec!(box Self, box Literal(Path::new_(
                        vec!["__D", "Error"], None, vec![], false
                    ))),
                    true
                )),
                attributes: Vec::new(),
                combine_substructure: combine_substructure(box |a, b, c| {
                    decodable_substructure(a, b, c, krate)
                }),
            })
    };

    trait_def.expand(cx, mitem, item, push)
}

fn decodable_substructure(cx: &mut ExtCtxt, trait_span: Span,
                          substr: &Substructure,
                          krate: &str) -> P<Expr> {
    let decoder = substr.nonself_args[0].clone();
    let recurse = vec!(cx.ident_of(krate),
                    cx.ident_of("Decodable"),
                    cx.ident_of("decode"));
    let exprdecode = cx.expr_path(cx.path_global(trait_span, recurse));
    // throw an underscore in front to suppress unused variable warnings
    let blkarg = cx.ident_of("_d");
    let blkdecoder = cx.expr_ident(trait_span, blkarg);

    return match *substr.fields {
        StaticStruct(_, ref summary) => {
            let nfields = match *summary {
                Unnamed(ref fields) => fields.len(),
                Named(ref fields) => fields.len()
            };
            let read_struct_field = cx.ident_of("read_struct_field");

            let path = cx.path_ident(trait_span, substr.type_ident);
            let result = decode_static_fields(cx,
                                              trait_span,
                                              path,
                                              summary,
                                              |cx, span, name, field| {
                cx.expr_try(span,
                    cx.expr_method_call(span, blkdecoder.clone(), read_struct_field,
                                        vec!(cx.expr_str(span, name),
                                          cx.expr_uint(span, field),
                                          exprdecode.clone())))
            });
            let result = cx.expr_ok(trait_span, result);
            cx.expr_method_call(trait_span,
                                decoder,
                                cx.ident_of("read_struct"),
                                vec!(
                cx.expr_str(trait_span, token::get_ident(substr.type_ident)),
                cx.expr_uint(trait_span, nfields),
                cx.lambda_expr_1(trait_span, result, blkarg)
            ))
        }
        StaticEnum(_, ref fields) => {
            let variant = cx.ident_of("i");

            let mut arms = Vec::new();
            let mut variants = Vec::new();
            let rvariant_arg = cx.ident_of("read_enum_variant_arg");

            for (i, &(name, v_span, ref parts)) in fields.iter().enumerate() {
                variants.push(cx.expr_str(v_span, token::get_ident(name)));

                let path = cx.path(trait_span, vec![substr.type_ident, name]);
                let decoded = decode_static_fields(cx,
                                                   v_span,
                                                   path,
                                                   parts,
                                                   |cx, span, _, field| {
                    let idx = cx.expr_uint(span, field);
                    cx.expr_try(span,
                        cx.expr_method_call(span, blkdecoder.clone(), rvariant_arg,
                                            vec!(idx, exprdecode.clone())))
                });

                arms.push(cx.arm(v_span,
                                 vec!(cx.pat_lit(v_span, cx.expr_uint(v_span, i))),
                                 decoded));
            }

            arms.push(cx.arm_unreachable(trait_span));

            let result = cx.expr_ok(trait_span,
                                    cx.expr_match(trait_span,
                                                  cx.expr_ident(trait_span, variant), arms));
            let lambda = cx.lambda_expr(trait_span, vec!(blkarg, variant), result);
            let variant_vec = cx.expr_vec(trait_span, variants);
            let variant_vec = cx.expr_addr_of(trait_span, variant_vec);
            let result = cx.expr_method_call(trait_span, blkdecoder,
                                             cx.ident_of("read_enum_variant"),
                                             vec!(variant_vec, lambda));
            cx.expr_method_call(trait_span,
                                decoder,
                                cx.ident_of("read_enum"),
                                vec!(
                cx.expr_str(trait_span, token::get_ident(substr.type_ident)),
                cx.lambda_expr_1(trait_span, result, blkarg)
            ))
        }
        _ => cx.bug("expected StaticEnum or StaticStruct in deriving(Decodable)")
    };
}

/// Create a decoder for a single enum variant/struct:
/// - `outer_pat_path` is the path to this enum variant/struct
/// - `getarg` should retrieve the `uint`-th field with name `@str`.
fn decode_static_fields<F>(cx: &mut ExtCtxt,
                           trait_span: Span,
                           outer_pat_path: ast::Path,
                           fields: &StaticFields,
                           mut getarg: F)
                           -> P<Expr> where
    F: FnMut(&mut ExtCtxt, Span, InternedString, uint) -> P<Expr>,
{
    match *fields {
        Unnamed(ref fields) => {
            let path_expr = cx.expr_path(outer_pat_path);
            if fields.is_empty() {
                path_expr
            } else {
                let fields = fields.iter().enumerate().map(|(i, &span)| {
                    getarg(cx, span,
                           token::intern_and_get_ident(&format!("_field{}", i)[]),
                           i)
                }).collect();

                cx.expr_call(trait_span, path_expr, fields)
            }
        }
        Named(ref fields) => {
            // use the field's span to get nicer error messages.
            let fields = fields.iter().enumerate().map(|(i, &(name, span))| {
                let arg = getarg(cx, span, token::get_ident(name), i);
                cx.field_imm(span, name, arg)
            }).collect();
            cx.expr_struct(trait_span, outer_pat_path, fields)
        }
    }
}
<|endoftext|>


<commit_before>#[feature(globs)];
#[feature(struct_variant)];
#[feature(macro_rules)];
extern mod std;
extern mod extra;
use extra::getopts;
use basics::*;
use policies::TimingPolicy;
use std::io::File;
use std::rt::shouldnt_be_public::SelectInner;
use std::select::Select;

mod basics;
mod policies;
mod raft;
mod sim;

struct SelectBox<'self> {
    inner: &'self mut std::select::Select,
}

impl<'self> SelectInner for SelectBox<'self> {
    fn optimistic_check(&mut self) -> bool {
        self.inner.optimistic_check()
    }
    // Returns true if data was available. If so, shall also wake() the task.
    fn block_on(&mut self, sched: &mut std::rt::sched::Scheduler, task: std::rt::BlockedTask) -> bool {
        self.inner.block_on(sched, task)
    }
    // Returns true if data was available.
    fn unblock_from(&mut self) -> bool {
        self.inner.unblock_from()
    }
}

impl<'self> std::select::Select for SelectBox<'self> {
}

fn main() {
    let args = std::os::args();
    let opts = [
        getopts::optflag("h"),
        getopts::optflag("help"),
        getopts::optopt("logs"),
        getopts::optopt("samples"),
        getopts::optopt("servers"),
        getopts::optopt("tasks"),
        getopts::optopt("timing"),
    ];
    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m },
        Err(f) => { fail!(f.to_err_msg()) },
    };
    let usage = || {
        println!("Usage: {} [options]", args[0]);
        println!("-h, --help       Print this help message");
        println!("--logs=POLICY    Log length policy (default same)");
        println!("--samples=N      Number of simulations (default 10,000)");
        println!("--servers=N      Simulate cluster with N servers");
        println!("--tasks=N        Number of parallel jobs (default {})",
                 std::rt::default_sched_threads() - 1);
        println!("--timing=POLICY  Network timing policy (default LAN)");
    };
    if matches.opt_present("h") || matches.opt_present("help") {
        usage();
        return;
    }
    if !matches.free.is_empty() {
        usage();
        fail!(format!("Extra arguments: {}", matches.free.len()));
    }
    let log_length : ~str = match matches.opt_str("logs") {
        Some(s) => { s },
        None => { ~"same" },
    };
    let num_samples : uint = match matches.opt_str("samples") {
        Some(s) => { match std::from_str::from_str(s) {
            Some(i) => { i },
            None => {
                usage();
                fail!(format!("Couldn't parse number of samples from '{}'", s));
            },
        }},
        None => { 10000 },
    };
    let num_servers : uint = match matches.opt_str("servers") {
        Some(s) => { match std::from_str::from_str(s) {
            Some(i) => { i },
            None => {
                usage();
                fail!(format!("Couldn't parse number of servers from '{}'", s));
            },
        }},
        None => { 5 },
    };
    let num_tasks : uint = match matches.opt_str("tasks") {
        Some(s) => { match std::from_str::from_str(s) {
            Some(i) => { i },
            None => {
                usage();
                fail!(format!("Couldn't parse number of tasks from '{}'", s));
            },
        }},
        None => { std::rt::default_sched_threads() - 1 },
    };
    let timing : ~str = match matches.opt_str("timing") {
        Some(s) => { s },
        None => { ~"LAN" },
    };

    let mut meta : ~[(~str, ~str)] = ~[];
    meta.push((~"log_length", log_length.clone()));
    meta.push((~"servers",    format!("{}", num_servers)));
    meta.push((~"tasks",      format!("{}", num_tasks)));
    meta.push((~"trials",     format!("{}", num_samples)));
    meta.push((~"timing",     timing.clone()));

    println!("Log length: {}", log_length)
    println!("Servers:    {}", num_servers)
    println!("Tasks:      {}", num_tasks)
    println!("Trials:     {}", num_samples)
    println!("Timing:     {}", timing)

    let start_ns = extra::time::precise_time_ns();

    let mut signals = std::io::signal::Listener::new();
    signals.register(std::io::signal::Interrupt);

    let samples = if num_tasks <= 1 {
        run_task(num_samples, num_servers, timing, log_length)
        // TODO: ctrl-c?
    } else {
        let (mut port, chan): (std::comm::Port<~[Time]>, std::comm::Chan<~[Time]>) = stream();
        let chan = std::comm::SharedChan::new(chan);
        for tid in range(0, num_tasks) {
            let child_chan = chan.clone();
            let child_log_length = log_length.clone();
            let child_timing = timing.clone();
            do spawn || {
                let n = if tid == 0 {
                    // get the odd one left over
                    num_samples - (num_samples / num_tasks) * (num_tasks - 1)
                } else {
                    num_samples / num_tasks
                };
                child_chan.send(run_task(n, num_servers, child_timing, child_log_length));
            }
        }

        let mut samples = ~[];
        samples.reserve(num_samples);
        let mut tasks_outstanding = num_tasks;
        while tasks_outstanding > 0 {
            //let (p1, c1): (std::rt::comm::Port<std::rt::io::signal::Signum>, std::rt::comm::Chan<std::rt::io::signal::Signum>) = std::rt::comm::stream();
            //let (p2, c2): (std::rt::comm::Port<~[uint]>, std::rt::comm::Chan<~[uint]>) = std::rt::comm::stream();
            //match std::select::select([p1 as &Select, p2 as &Select]) {
            match std::select::select([SelectBox{ inner: &mut signals.port.x as &mut std::select::Select }, SelectBox{ inner: &mut port.x as &mut std::select::Select }]) {
                0 => match signals.port.recv() {
                    std::io::signal::Interrupt => {
                        println!("Should exit now");
                    },
                    _ => fail!("Unexpected signal"),
                },
                1 => {
                    println!("Task completed");
                    tasks_outstanding -= 1;
                    samples.push_all(port.recv());
                },
                _ => fail!("Unexpected return value from select")
            }
        }

        //do num_tasks.times {
        //    samples.push_all(port.recv());
        //}
        extra::sort::tim_sort(samples);
        samples
    };

    let end_ns = extra::time::precise_time_ns();
    let elapsed_ns = end_ns - start_ns;
    meta.push((~"wall",  format!("{}", elapsed_ns as f64 / 1e9)));

    println!("Min:     {} ticks", samples[0]);
    println!("Median:  {} ticks", samples[samples.len()/2]);
    println!("Max:     {} ticks", samples[samples.len()-1]);
    println!("Wall:    {:.2f} s",     elapsed_ns as f64 / 1e9);

    let metaf = &mut File::create(&Path::new("meta.csv")).unwrap() as &mut Writer;
    for &(ref k, ref _v) in meta.iter() {
        write!(metaf, "{}, ", *k);
    }
    write!(metaf, "\n");
    for &(ref _k, ref v) in meta.iter() {
        write!(metaf, "{}, ", *v);
    }
    write!(metaf, "\n");

    let runf = &mut File::create(&Path::new("samples.csv")).unwrap() as &mut Writer;
    writeln!(runf, "election_time");
    for sample in samples.iter() {
        writeln!(runf, "{}", *sample);
    }
}

fn run_task(n: uint, num_servers: uint, timing: &str, log_length: &str) -> ~[Time] {
    let mut samples = ~[];
    samples.reserve(n);
    do n.times {
        // TODO: may be better to reuse these timing policies.
        let p = policies::make(timing);
        let sample = sim::simulate(num_servers, p, log_length);
        samples.push(sample);
    }
    extra::sort::tim_sort(samples);
    return samples;
}
<commit_msg>Add selectmany macro<commit_after>#[feature(globs)];
#[feature(struct_variant)];
#[feature(macro_rules)];
extern mod std;
extern mod extra;
use extra::getopts;
use basics::*;
use policies::TimingPolicy;
use std::io::File;
use std::rt::shouldnt_be_public::SelectInner;
use std::select::Select;

mod basics;
mod policies;
mod raft;
mod sim;

struct SelectBox<'self> {
    inner: &'self mut std::select::Select,
}

impl<'self> SelectInner for SelectBox<'self> {
    fn optimistic_check(&mut self) -> bool {
        self.inner.optimistic_check()
    }
    // Returns true if data was available. If so, shall also wake() the task.
    fn block_on(&mut self, sched: &mut std::rt::sched::Scheduler, task: std::rt::BlockedTask) -> bool {
        self.inner.block_on(sched, task)
    }
    // Returns true if data was available.
    fn unblock_from(&mut self) -> bool {
        self.inner.unblock_from()
    }
}

impl<'self> std::select::Select for SelectBox<'self> {
}

macro_rules! selectmany_rec(
    ($index:expr,
     $port:expr => $action:expr,
     $( $port_rest:expr => $action_rest:expr ),+) => (
        if $index == 0 {
            $action
        } else {
            $index -= 1;
            selectmany_rec!($index, $( $port_rest => $action_rest ),*)
        }
    );
    ($index:expr, $port:expr => $action:expr) => (
        if $index == 0 {
            $action
        } else {
            fail!("Bogus return value from select()");
        }
    );
)

macro_rules! selectmany(
     ($( $port:expr => $action:expr ),+) => (
        do 1.times {
            let mut selectmany_i : uint = std::select::select([$( SelectBox{ inner: &mut $port as &mut std::select::Select } ),+]);
            selectmany_rec!(selectmany_i, $( $port => $action ),+)
        }
    );
)

fn main() {
    let args = std::os::args();
    let opts = [
        getopts::optflag("h"),
        getopts::optflag("help"),
        getopts::optopt("logs"),
        getopts::optopt("samples"),
        getopts::optopt("servers"),
        getopts::optopt("tasks"),
        getopts::optopt("timing"),
    ];
    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m },
        Err(f) => { fail!(f.to_err_msg()) },
    };
    let usage = || {
        println!("Usage: {} [options]", args[0]);
        println!("-h, --help       Print this help message");
        println!("--logs=POLICY    Log length policy (default same)");
        println!("--samples=N      Number of simulations (default 10,000)");
        println!("--servers=N      Simulate cluster with N servers");
        println!("--tasks=N        Number of parallel jobs (default {})",
                 std::rt::default_sched_threads() - 1);
        println!("--timing=POLICY  Network timing policy (default LAN)");
    };
    if matches.opt_present("h") || matches.opt_present("help") {
        usage();
        return;
    }
    if !matches.free.is_empty() {
        usage();
        fail!(format!("Extra arguments: {}", matches.free.len()));
    }
    let log_length : ~str = match matches.opt_str("logs") {
        Some(s) => { s },
        None => { ~"same" },
    };
    let num_samples : uint = match matches.opt_str("samples") {
        Some(s) => { match std::from_str::from_str(s) {
            Some(i) => { i },
            None => {
                usage();
                fail!(format!("Couldn't parse number of samples from '{}'", s));
            },
        }},
        None => { 10000 },
    };
    let num_servers : uint = match matches.opt_str("servers") {
        Some(s) => { match std::from_str::from_str(s) {
            Some(i) => { i },
            None => {
                usage();
                fail!(format!("Couldn't parse number of servers from '{}'", s));
            },
        }},
        None => { 5 },
    };
    let num_tasks : uint = match matches.opt_str("tasks") {
        Some(s) => { match std::from_str::from_str(s) {
            Some(i) => { i },
            None => {
                usage();
                fail!(format!("Couldn't parse number of tasks from '{}'", s));
            },
        }},
        None => { std::rt::default_sched_threads() - 1 },
    };
    let timing : ~str = match matches.opt_str("timing") {
        Some(s) => { s },
        None => { ~"LAN" },
    };

    let mut meta : ~[(~str, ~str)] = ~[];
    meta.push((~"log_length", log_length.clone()));
    meta.push((~"servers",    format!("{}", num_servers)));
    meta.push((~"tasks",      format!("{}", num_tasks)));
    meta.push((~"trials",     format!("{}", num_samples)));
    meta.push((~"timing",     timing.clone()));

    println!("Log length: {}", log_length)
    println!("Servers:    {}", num_servers)
    println!("Tasks:      {}", num_tasks)
    println!("Trials:     {}", num_samples)
    println!("Timing:     {}", timing)

    let start_ns = extra::time::precise_time_ns();

    let mut signals = std::io::signal::Listener::new();
    signals.register(std::io::signal::Interrupt);

    let samples = if num_tasks <= 1 {
        run_task(num_samples, num_servers, timing, log_length)
        // TODO: ctrl-c?
    } else {
        let (mut port, chan): (std::comm::Port<~[Time]>, std::comm::Chan<~[Time]>) = stream();
        let chan = std::comm::SharedChan::new(chan);
        for tid in range(0, num_tasks) {
            let child_chan = chan.clone();
            let child_log_length = log_length.clone();
            let child_timing = timing.clone();
            do spawn || {
                let n = if tid == 0 {
                    // get the odd one left over
                    num_samples - (num_samples / num_tasks) * (num_tasks - 1)
                } else {
                    num_samples / num_tasks
                };
                child_chan.send(run_task(n, num_servers, child_timing, child_log_length));
            }
        }

        let mut samples = ~[];
        samples.reserve(num_samples);
        let mut tasks_outstanding = num_tasks;
        while tasks_outstanding > 0 {
            //let (p1, c1): (std::rt::comm::Port<std::rt::io::signal::Signum>, std::rt::comm::Chan<std::rt::io::signal::Signum>) = std::rt::comm::stream();
            //let (p2, c2): (std::rt::comm::Port<~[uint]>, std::rt::comm::Chan<~[uint]>) = std::rt::comm::stream();
            //match std::select::select([p1 as &Select, p2 as &Select]) {

            selectmany!(
                signals.port.x => {
                    match signals.port.recv() {
                        std::io::signal::Interrupt => {
                            println!("Should exit now");
                        },
                        _ => fail!("Unexpected signal"),
                    }
                },
                port.x => {
                    println!("Task completed");
                    tasks_outstanding -= 1;
                    samples.push_all(port.recv());
                }
            )
        }

        //do num_tasks.times {
        //    samples.push_all(port.recv());
        //}
        extra::sort::tim_sort(samples);
        samples
    };

    let end_ns = extra::time::precise_time_ns();
    let elapsed_ns = end_ns - start_ns;
    meta.push((~"wall",  format!("{}", elapsed_ns as f64 / 1e9)));

    println!("Min:     {} ticks", samples[0]);
    println!("Median:  {} ticks", samples[samples.len()/2]);
    println!("Max:     {} ticks", samples[samples.len()-1]);
    println!("Wall:    {:.2f} s",     elapsed_ns as f64 / 1e9);

    let metaf = &mut File::create(&Path::new("meta.csv")).unwrap() as &mut Writer;
    for &(ref k, ref _v) in meta.iter() {
        write!(metaf, "{}, ", *k);
    }
    write!(metaf, "\n");
    for &(ref _k, ref v) in meta.iter() {
        write!(metaf, "{}, ", *v);
    }
    write!(metaf, "\n");

    let runf = &mut File::create(&Path::new("samples.csv")).unwrap() as &mut Writer;
    writeln!(runf, "election_time");
    for sample in samples.iter() {
        writeln!(runf, "{}", *sample);
    }
}

fn run_task(n: uint, num_servers: uint, timing: &str, log_length: &str) -> ~[Time] {
    let mut samples = ~[];
    samples.reserve(n);
    do n.times {
        // TODO: may be better to reuse these timing policies.
        let p = policies::make(timing);
        let sample = sim::simulate(num_servers, p, log_length);
        samples.push(sample);
    }
    extra::sort::tim_sort(samples);
    return samples;
}
<|endoftext|>