<commit_before>use std::collections::HashSet;

use dora_parser::ast::visit::{walk_file, Visitor};
use dora_parser::ast::{Ast, Enum, File, TypeParam};

use crate::error::msg::SemError;
use crate::semck;
use crate::sym::TypeSym;
use crate::ty::BuiltinType;
use crate::vm::{EnumId, EnumVariant, NodeMap, VM};

pub fn check<'ast>(vm: &mut VM<'ast>, ast: &'ast Ast, map_enum_defs: &NodeMap<EnumId>) {
    let mut enumck = EnumCheck {
        vm,
        ast,
        map_enum_defs,
        file_id: 0,

        enum_id: None,
    };

    enumck.check();
}

struct EnumCheck<'x, 'ast: 'x> {
    vm: &'x mut VM<'ast>,
    ast: &'ast Ast,
    map_enum_defs: &'x NodeMap<EnumId>,
    file_id: u32,

    enum_id: Option<EnumId>,
}

impl<'x, 'ast> EnumCheck<'x, 'ast> {
    fn check(&mut self) {
        self.visit_ast(self.ast);
    }

    fn check_type_params(&mut self, ast: &'ast Enum, type_params: &'ast [TypeParam]) {
        let enum_id = self.enum_id.expect("missing enum_id");
        let xenum = &self.vm.enums[enum_id];
        let mut xenum = xenum.write();

        if type_params.len() > 0 {
            let mut names = HashSet::new();
            let mut type_param_id = 0;
            let mut params = Vec::new();

            for type_param in type_params {
                if !names.insert(type_param.name) {
                    let name = self.vm.interner.str(type_param.name).to_string();
                    let msg = SemError::TypeParamNameNotUnique(name);
                    self.vm.diag.lock().report(xenum.file, type_param.pos, msg);
                }

                params.push(BuiltinType::TypeParam(type_param_id.into()));

                for bound in &type_param.bounds {
                    let ty = semck::read_type(self.vm, xenum.file, bound);

                    match ty {
                        Some(BuiltinType::TraitObject(trait_id)) => {
                            if !xenum.type_params[type_param_id]
                                .trait_bounds
                                .insert(trait_id)
                            {
                                let msg = SemError::DuplicateTraitBound;
                                self.vm.diag.lock().report(xenum.file, type_param.pos, msg);
                            }
                        }

                        None => {
                            // unknown type, error is already thrown
                        }

                        _ => {
                            let msg = SemError::BoundExpected;
                            self.vm.diag.lock().report(xenum.file, bound.pos(), msg);
                        }
                    }
                }

                let sym = TypeSym::SymTypeParam(type_param_id.into());
                self.vm.sym.lock().insert_type(type_param.name, sym);
                type_param_id += 1;
            }
        } else {
            let msg = SemError::TypeParamsExpected;
            self.vm.diag.lock().report(xenum.file, ast.pos, msg);
        }
    }
}

impl<'x, 'ast> Visitor<'ast> for EnumCheck<'x, 'ast> {
    fn visit_file(&mut self, f: &'ast File) {
        walk_file(self, f);
        self.file_id += 1;
    }

    fn visit_enum(&mut self, e: &'ast Enum) {
        let enum_id = *self.map_enum_defs.get(e.id).unwrap();
        self.enum_id = Some(enum_id);

        if let Some(ref type_params) = e.type_params {
            self.check_type_params(e, type_params);
        }

        let xenum = &self.vm.enums[enum_id];
        let mut xenum = xenum.write();

        let mut next_variant_id: u32 = 0;

        for value in &e.variants {
            let mut types: Vec<BuiltinType> = Vec::new();

            if let Some(ref variant_types) = value.types {
                for ty in variant_types {
                    let variant_ty = semck::read_type(self.vm, self.file_id.into(), ty)
                        .unwrap_or(BuiltinType::Error);
                    types.push(variant_ty);
                }
            }

            let variant = EnumVariant {
                name: value.name,
                types: types,
            };
            xenum.variants.push(variant);
            let result = xenum.name_to_value.insert(value.name, next_variant_id);

            if result.is_some() {
                let name = self.vm.interner.str(value.name).to_string();
                self.vm
                    .diag
                    .lock()
                    .report(xenum.file, value.pos, SemError::ShadowEnumValue(name));
            }

            next_variant_id += 1;
        }

        if e.variants.is_empty() {
            self.vm
                .diag
                .lock()
                .report(xenum.file, e.pos, SemError::NoEnumValue);
        }

        self.enum_id = None;
    }
}

#[cfg(test)]
mod tests {
    use crate::error::msg::SemError;
    use crate::semck::tests::*;

    #[test]
    fn enum_definitions() {
        err("enum Foo {}", pos(1, 1), SemError::NoEnumValue);
        ok("enum Foo { A, B, C }");
        err(
            "enum Foo { A, A }",
            pos(1, 15),
            SemError::ShadowEnumValue("A".into()),
        );
    }

    #[test]
    fn enum_with_argument() {
        ok("
            enum Foo { A(Int32), B(Float32), C}
            fun give_me_a(): Foo { Foo::A(1) }
            fun give_me_b(): Foo { Foo::B(2.0F) }
            fun give_me_c(): Foo { Foo::C }

        ");
    }

    #[test]
    fn enum_wrong_type() {
        err(
            "
            enum Foo { A(Int32), B(Float32), C}
            fun give_me_a(): Foo { Foo::A(2.0F) }

        ",
            pos(3, 42),
            SemError::EnumArgsIncompatible(
                "Foo".into(),
                "A".into(),
                vec!["Int32".into()],
                vec!["Float32".into()],
            ),
        );
    }

    #[test]
    fn enum_missing_args() {
        err(
            "
            enum Foo { A(Int32), B(Float32), C}
            fun give_me_a(): Foo { Foo::A }

        ",
            pos(3, 39),
            SemError::EnumArgsIncompatible(
                "Foo".into(),
                "A".into(),
                vec!["Int32".into()],
                Vec::new(),
            ),
        );
    }

    #[test]
    fn enum_unexpected_args() {
        err(
            "
            enum Foo { A(Int32), B(Float32), C}
            fun give_me_c(): Foo { Foo::C(12.0F) }

        ",
            pos(3, 42),
            SemError::EnumArgsIncompatible(
                "Foo".into(),
                "C".into(),
                Vec::new(),
                vec!["Float32".into()],
            ),
        );
    }

    #[test]
    fn enum_parens_but_no_args() {
        err(
            "
            enum Foo { A(Int32), B(Float32), C}
            fun give_me_c(): Foo { Foo::C() }
        ",
            pos(3, 42),
            SemError::EnumArgsNoParens("Foo".into(), "C".into()),
        );
    }

    #[test]
    fn enum_copy() {
        ok("
            enum Foo { A(Int32), B(Float32), C}
            fun foo_test(y: Foo): Foo { let x: Foo = y; x }
        ");
    }

    #[test]
    #[ignore]
    fn enum_generic() {
        ok("
            enum Foo[T] { One(T), Two }
        ");
    }

    #[test]
    fn enum_with_type_param() {
        ok("trait SomeTrait {} enum MyOption[T: SomeTrait] { None, Some(T) }");
    }
}
<commit_msg>enum: add tests for various failures<commit_after>use std::collections::HashSet;

use dora_parser::ast::visit::{walk_file, Visitor};
use dora_parser::ast::{Ast, Enum, File, TypeParam};

use crate::error::msg::SemError;
use crate::semck;
use crate::sym::TypeSym;
use crate::ty::BuiltinType;
use crate::vm::{EnumId, EnumVariant, NodeMap, VM};

pub fn check<'ast>(vm: &mut VM<'ast>, ast: &'ast Ast, map_enum_defs: &NodeMap<EnumId>) {
    let mut enumck = EnumCheck {
        vm,
        ast,
        map_enum_defs,
        file_id: 0,

        enum_id: None,
    };

    enumck.check();
}

struct EnumCheck<'x, 'ast: 'x> {
    vm: &'x mut VM<'ast>,
    ast: &'ast Ast,
    map_enum_defs: &'x NodeMap<EnumId>,
    file_id: u32,

    enum_id: Option<EnumId>,
}

impl<'x, 'ast> EnumCheck<'x, 'ast> {
    fn check(&mut self) {
        self.visit_ast(self.ast);
    }

    fn check_type_params(&mut self, ast: &'ast Enum, type_params: &'ast [TypeParam]) {
        let enum_id = self.enum_id.expect("missing enum_id");
        let xenum = &self.vm.enums[enum_id];
        let mut xenum = xenum.write();

        if type_params.len() > 0 {
            let mut names = HashSet::new();
            let mut type_param_id = 0;
            let mut params = Vec::new();

            for type_param in type_params {
                if !names.insert(type_param.name) {
                    let name = self.vm.interner.str(type_param.name).to_string();
                    let msg = SemError::TypeParamNameNotUnique(name);
                    self.vm.diag.lock().report(xenum.file, type_param.pos, msg);
                }

                params.push(BuiltinType::TypeParam(type_param_id.into()));

                for bound in &type_param.bounds {
                    let ty = semck::read_type(self.vm, xenum.file, bound);

                    match ty {
                        Some(BuiltinType::TraitObject(trait_id)) => {
                            if !xenum.type_params[type_param_id]
                                .trait_bounds
                                .insert(trait_id)
                            {
                                let msg = SemError::DuplicateTraitBound;
                                self.vm.diag.lock().report(xenum.file, type_param.pos, msg);
                            }
                        }

                        None => {
                            // unknown type, error is already thrown
                        }

                        _ => {
                            let msg = SemError::BoundExpected;
                            self.vm.diag.lock().report(xenum.file, bound.pos(), msg);
                        }
                    }
                }

                let sym = TypeSym::SymTypeParam(type_param_id.into());
                self.vm.sym.lock().insert_type(type_param.name, sym);
                type_param_id += 1;
            }
        } else {
            let msg = SemError::TypeParamsExpected;
            self.vm.diag.lock().report(xenum.file, ast.pos, msg);
        }
    }
}

impl<'x, 'ast> Visitor<'ast> for EnumCheck<'x, 'ast> {
    fn visit_file(&mut self, f: &'ast File) {
        walk_file(self, f);
        self.file_id += 1;
    }

    fn visit_enum(&mut self, e: &'ast Enum) {
        let enum_id = *self.map_enum_defs.get(e.id).unwrap();
        self.enum_id = Some(enum_id);

        if let Some(ref type_params) = e.type_params {
            self.check_type_params(e, type_params);
        }

        let xenum = &self.vm.enums[enum_id];
        let mut xenum = xenum.write();

        let mut next_variant_id: u32 = 0;

        for value in &e.variants {
            let mut types: Vec<BuiltinType> = Vec::new();

            if let Some(ref variant_types) = value.types {
                for ty in variant_types {
                    let variant_ty = semck::read_type(self.vm, self.file_id.into(), ty)
                        .unwrap_or(BuiltinType::Error);
                    types.push(variant_ty);
                }
            }

            let variant = EnumVariant {
                name: value.name,
                types: types,
            };
            xenum.variants.push(variant);
            let result = xenum.name_to_value.insert(value.name, next_variant_id);

            if result.is_some() {
                let name = self.vm.interner.str(value.name).to_string();
                self.vm
                    .diag
                    .lock()
                    .report(xenum.file, value.pos, SemError::ShadowEnumValue(name));
            }

            next_variant_id += 1;
        }

        if e.variants.is_empty() {
            self.vm
                .diag
                .lock()
                .report(xenum.file, e.pos, SemError::NoEnumValue);
        }

        self.enum_id = None;
    }
}

#[cfg(test)]
mod tests {
    use crate::error::msg::SemError;
    use crate::semck::tests::*;

    #[test]
    fn enum_definitions() {
        err("enum Foo {}", pos(1, 1), SemError::NoEnumValue);
        ok("enum Foo { A, B, C }");
        err(
            "enum Foo { A, A }",
            pos(1, 15),
            SemError::ShadowEnumValue("A".into()),
        );
    }

    #[test]
    fn enum_with_argument() {
        ok("
            enum Foo { A(Int32), B(Float32), C}
            fun give_me_a(): Foo { Foo::A(1) }
            fun give_me_b(): Foo { Foo::B(2.0F) }
            fun give_me_c(): Foo { Foo::C }

        ");
    }

    #[test]
    fn enum_wrong_type() {
        err(
            "
            enum Foo { A(Int32), B(Float32), C}
            fun give_me_a(): Foo { Foo::A(2.0F) }

        ",
            pos(3, 42),
            SemError::EnumArgsIncompatible(
                "Foo".into(),
                "A".into(),
                vec!["Int32".into()],
                vec!["Float32".into()],
            ),
        );
    }

    #[test]
    fn enum_missing_args() {
        err(
            "
            enum Foo { A(Int32), B(Float32), C}
            fun give_me_a(): Foo { Foo::A }

        ",
            pos(3, 39),
            SemError::EnumArgsIncompatible(
                "Foo".into(),
                "A".into(),
                vec!["Int32".into()],
                Vec::new(),
            ),
        );
    }

    #[test]
    fn enum_unexpected_args() {
        err(
            "
            enum Foo { A(Int32), B(Float32), C}
            fun give_me_c(): Foo { Foo::C(12.0F) }

        ",
            pos(3, 42),
            SemError::EnumArgsIncompatible(
                "Foo".into(),
                "C".into(),
                Vec::new(),
                vec!["Float32".into()],
            ),
        );
    }

    #[test]
    fn enum_parens_but_no_args() {
        err(
            "
            enum Foo { A(Int32), B(Float32), C}
            fun give_me_c(): Foo { Foo::C() }
        ",
            pos(3, 42),
            SemError::EnumArgsNoParens("Foo".into(), "C".into()),
        );
    }

    #[test]
    fn enum_copy() {
        ok("
            enum Foo { A(Int32), B(Float32), C}
            fun foo_test(y: Foo): Foo { let x: Foo = y; x }
        ");
    }

    #[test]
    #[ignore]
    fn enum_generic() {
        ok("
            enum Foo[T] { One(T), Two }
        ");
    }

    #[test]
    fn enum_with_type_param() {
        ok("trait SomeTrait {} enum MyOption[T: SomeTrait] { None, Some(T) }");
    }

    #[test]
    fn enum_generic_with_failures() {
        err(
            "enum MyOption[] { A, B }",
            pos(1, 1),
            SemError::TypeParamsExpected,
        );

        err(
            "enum MyOption[X, X] { A, B }",
            pos(1, 18),
            SemError::TypeParamNameNotUnique("X".into()),
        );

        err(
            "enum MyOption[X: NonExistingTrait] { A, B }",
            pos(1, 18),
            SemError::UnknownType("NonExistingTrait".into()),
        );
    }
}
<|endoftext|>


<commit_before>//! Translators for various architectures to Falcon IL.
//!
//! Translators in Falcon do not lift individual instructions, but instead lift
//! basic blocks. This is both more performant than lifting individual
//! instructions, and allows Falcon to deal with weird cases such as the delay
//! slot in the MIPS architecture.
//!
//! Translators lift individual instructions to `ControlFlowGraph`, and combine
//! these graphs to form a single block. A single instruction may lift to not
//! only multiple Falcon IL instructions, but also multiple IL blocks.
//!
//! Instructions for direct branches in Falcon IL are omitted in the IL, and
//! instead edges with conditional guards are emitted. The Brc operation is only
//! emitted for indirect branches, and instructions which are typically used to
//! call other functions.
//!
//! If you are lifting directly from loader (Elf/PE/other), you do not need to
//! pay attention to the translators. The correct translator will be chosen
//! automatically.

use crate::memory::MemoryPermissions;

mod block_translation_result;
pub mod mips;
mod options;
pub mod ppc;
pub mod x86;

use crate::error::*;
use crate::il;
use crate::il::*;
pub use block_translation_result::BlockTranslationResult;
use falcon_capstone::capstone;
pub use options::{Options, OptionsBuilder};
use std::collections::{BTreeMap, VecDeque};
pub(crate) const DEFAULT_TRANSLATION_BLOCK_BYTES: usize = 64;

/// This trait is used by the translator to continually find and lift bytes from an underlying
/// memory model.
///
/// Anything that implements this trait can be used as a memory backing for lifting.
pub trait TranslationMemory {
    fn permissions(&self, address: u64) -> Option<MemoryPermissions>;

    fn get_u8(&self, address: u64) -> Option<u8>;

    fn get_bytes(&self, address: u64, length: usize) -> Vec<u8> {
        let mut bytes = Vec::new();
        for i in 0..length {
            match self.permissions(address) {
                Some(permissions) => {
                    if !permissions.contains(MemoryPermissions::EXECUTE) {
                        break;
                    }
                }
                None => break,
            }
            match self.get_u8(address + i as u64) {
                Some(u) => bytes.push(u),
                None => break,
            };
        }
        bytes
    }
}

// A convenience function for turning unhandled instructions into intrinsics
pub(crate) fn unhandled_intrinsic(
    control_flow_graph: &mut il::ControlFlowGraph,
    instruction: &capstone::Instr,
) -> Result<()> {
    let block_index = {
        let block = control_flow_graph.new_block()?;

        block.intrinsic(il::Intrinsic::new(
            instruction.mnemonic.clone(),
            format!("{} {}", instruction.mnemonic, instruction.op_str),
            Vec::new(),
            None,
            None,
            instruction.bytes.get(0..4).unwrap().to_vec(),
        ));

        block.index()
    };

    control_flow_graph.set_entry(block_index)?;
    control_flow_graph.set_exit(block_index)?;

    Ok(())
}

/// A generic translation trait, implemented by various architectures.
pub trait Translator {
    /// Translates a basic block
    fn translate_block(
        &self,
        bytes: &[u8],
        address: u64,
        options: &Options,
    ) -> Result<BlockTranslationResult>;

    /// Translates a function
    fn translate_function(
        &self,
        memory: &dyn TranslationMemory,
        function_address: u64,
    ) -> Result<Function> {
        self.translate_function_extended(memory, function_address, &Options::default())
    }

    /// Translates a function
    ///
    /// Provides additional options over translate_function
    fn translate_function_extended(
        &self,
        memory: &dyn TranslationMemory,
        function_address: u64,
        options: &Options,
    ) -> Result<Function> {
        // Addresses of blocks pending translation
        let mut translation_queue: VecDeque<u64> = VecDeque::new();

        // The results of block translations
        let mut translation_results: BTreeMap<u64, BlockTranslationResult> = BTreeMap::new();

        translation_queue.push_front(function_address);

        options.manual_edges().iter().for_each(|manual_edge| {
            translation_queue.push_back(manual_edge.head_address());
            translation_queue.push_back(manual_edge.tail_address());
        });

        // translate all blocks in the function
        while !translation_queue.is_empty() {
            let block_address = translation_queue.pop_front().unwrap();

            if translation_results.contains_key(&block_address) {
                continue;
            }

            let block_bytes = memory.get_bytes(block_address, DEFAULT_TRANSLATION_BLOCK_BYTES);
            if block_bytes.is_empty() {
                let mut control_flow_graph = ControlFlowGraph::new();
                let block_index = control_flow_graph.new_block()?.index();
                control_flow_graph.set_entry(block_index)?;
                control_flow_graph.set_exit(block_index)?;
                translation_results.insert(
                    block_address,
                    BlockTranslationResult::new(
                        vec![(block_address, control_flow_graph)],
                        block_address,
                        0,
                        Vec::new(),
                    ),
                );
                continue;
            }

            // translate this block
            let block_translation_result =
                self.translate_block(&block_bytes, block_address, options)?;

            // enqueue all successors
            for successor in block_translation_result.successors().iter() {
                if !translation_queue.contains(&successor.0) {
                    translation_queue.push_back(successor.0);
                }
            }

            translation_results.insert(block_address, block_translation_result);
        }

        // We now insert all of these blocks into a new control flow graph,
        // keeping track of their new entry and exit indices.

        // A mapping of instruction address to entry/exit vertex indices
        let mut instruction_indices: BTreeMap<u64, (usize, usize)> = BTreeMap::new();

        // A mapping of block address to entry/exit vertex indices;
        let mut block_indices: BTreeMap<u64, (usize, usize)> = BTreeMap::new();

        let mut control_flow_graph = ControlFlowGraph::new();
        for result in &translation_results {
            let block_translation_result = result.1;
            let mut block_entry = 0;
            let mut block_exit = 0;
            let mut previous_exit = None;
            for &(address, ref instruction_graph) in block_translation_result.instructions().iter()
            {
                // Have we already inserted this instruction?
                let (entry, exit) = if instruction_indices.get(&address).is_some() {
                    instruction_indices[&address]
                } else {
                    let (entry, exit) = control_flow_graph.insert(instruction_graph)?;
                    instruction_indices.insert(address, (entry, exit));
                    (entry, exit)
                };
                // If this is not our first instruction through this block.
                if let Some(previous_exit) = previous_exit {
                    // If an edge from the previous block to this block doesn't
                    // exist
                    if control_flow_graph.edge(previous_exit, entry).is_err() {
                        // Create an edge from the previous block to this block.
                        control_flow_graph.unconditional_edge(previous_exit, entry)?;
                    }
                }
                // Our first instruction through this block
                else {
                    block_entry = entry;
                }
                block_exit = exit;
                previous_exit = Some(exit);
            }
            block_indices.insert(*result.0, (block_entry, block_exit));
        }

        // Insert the edges

        // Start with edges for our manual edges
        for manual_edge in options.manual_edges() {
            let (_, edge_head) = block_indices[&manual_edge.head_address()];
            let (edge_tail, _) = block_indices[&manual_edge.tail_address()];

            if control_flow_graph.edge(edge_head, edge_tail).is_ok() {
                continue;
            }

            if let Some(condition) = manual_edge.condition() {
                control_flow_graph.conditional_edge(edge_head, edge_tail, condition.clone())?;
            } else {
                control_flow_graph.unconditional_edge(edge_head, edge_tail)?;
            }
        }

        // For every block translation result
        for (address, block_translation_result) in translation_results {
            // Get the exit index for the last/tail vertex in this block
            let (_, block_exit) = block_indices[&address];
            // For every successor in the block translation result (this is an
            // (address, condition) tuple)
            for (successor_address, successor_condition) in
                block_translation_result.successors().iter()
            {
                // get the entry index for the first/head block in the successor
                let (block_entry, _) = block_indices[successor_address];
                // check for duplicate edges
                if control_flow_graph.edge(block_exit, block_entry).is_ok() {
                    continue;
                }
                match successor_condition {
                    Some(ref condition) => control_flow_graph.conditional_edge(
                        block_exit,
                        block_entry,
                        condition.clone(),
                    )?,
                    None => control_flow_graph.unconditional_edge(block_exit, block_entry)?,
                }
            }
        }

        // One block is the start of our control_flow_graph
        control_flow_graph.set_entry(block_indices[&function_address].0)?;

        // merge for the user
        control_flow_graph.merge()?;

        Ok(Function::new(function_address, control_flow_graph))
    }
}
<commit_msg>expose falcon::translator::ManualEdge, which should be pub (#101)<commit_after>//! Translators for various architectures to Falcon IL.
//!
//! Translators in Falcon do not lift individual instructions, but instead lift
//! basic blocks. This is both more performant than lifting individual
//! instructions, and allows Falcon to deal with weird cases such as the delay
//! slot in the MIPS architecture.
//!
//! Translators lift individual instructions to `ControlFlowGraph`, and combine
//! these graphs to form a single block. A single instruction may lift to not
//! only multiple Falcon IL instructions, but also multiple IL blocks.
//!
//! Instructions for direct branches in Falcon IL are omitted in the IL, and
//! instead edges with conditional guards are emitted. The Brc operation is only
//! emitted for indirect branches, and instructions which are typically used to
//! call other functions.
//!
//! If you are lifting directly from loader (Elf/PE/other), you do not need to
//! pay attention to the translators. The correct translator will be chosen
//! automatically.

use crate::memory::MemoryPermissions;

mod block_translation_result;
pub mod mips;
mod options;
pub mod ppc;
pub mod x86;

use crate::error::*;
use crate::il;
use crate::il::*;
pub use block_translation_result::BlockTranslationResult;
use falcon_capstone::capstone;
pub use options::{ManualEdge, Options, OptionsBuilder};
use std::collections::{BTreeMap, VecDeque};
pub(crate) const DEFAULT_TRANSLATION_BLOCK_BYTES: usize = 64;

/// This trait is used by the translator to continually find and lift bytes from an underlying
/// memory model.
///
/// Anything that implements this trait can be used as a memory backing for lifting.
pub trait TranslationMemory {
    fn permissions(&self, address: u64) -> Option<MemoryPermissions>;

    fn get_u8(&self, address: u64) -> Option<u8>;

    fn get_bytes(&self, address: u64, length: usize) -> Vec<u8> {
        let mut bytes = Vec::new();
        for i in 0..length {
            match self.permissions(address) {
                Some(permissions) => {
                    if !permissions.contains(MemoryPermissions::EXECUTE) {
                        break;
                    }
                }
                None => break,
            }
            match self.get_u8(address + i as u64) {
                Some(u) => bytes.push(u),
                None => break,
            };
        }
        bytes
    }
}

// A convenience function for turning unhandled instructions into intrinsics
pub(crate) fn unhandled_intrinsic(
    control_flow_graph: &mut il::ControlFlowGraph,
    instruction: &capstone::Instr,
) -> Result<()> {
    let block_index = {
        let block = control_flow_graph.new_block()?;

        block.intrinsic(il::Intrinsic::new(
            instruction.mnemonic.clone(),
            format!("{} {}", instruction.mnemonic, instruction.op_str),
            Vec::new(),
            None,
            None,
            instruction.bytes.get(0..4).unwrap().to_vec(),
        ));

        block.index()
    };

    control_flow_graph.set_entry(block_index)?;
    control_flow_graph.set_exit(block_index)?;

    Ok(())
}

/// A generic translation trait, implemented by various architectures.
pub trait Translator {
    /// Translates a basic block
    fn translate_block(
        &self,
        bytes: &[u8],
        address: u64,
        options: &Options,
    ) -> Result<BlockTranslationResult>;

    /// Translates a function
    fn translate_function(
        &self,
        memory: &dyn TranslationMemory,
        function_address: u64,
    ) -> Result<Function> {
        self.translate_function_extended(memory, function_address, &Options::default())
    }

    /// Translates a function
    ///
    /// Provides additional options over translate_function
    fn translate_function_extended(
        &self,
        memory: &dyn TranslationMemory,
        function_address: u64,
        options: &Options,
    ) -> Result<Function> {
        // Addresses of blocks pending translation
        let mut translation_queue: VecDeque<u64> = VecDeque::new();

        // The results of block translations
        let mut translation_results: BTreeMap<u64, BlockTranslationResult> = BTreeMap::new();

        translation_queue.push_front(function_address);

        options.manual_edges().iter().for_each(|manual_edge| {
            translation_queue.push_back(manual_edge.head_address());
            translation_queue.push_back(manual_edge.tail_address());
        });

        // translate all blocks in the function
        while !translation_queue.is_empty() {
            let block_address = translation_queue.pop_front().unwrap();

            if translation_results.contains_key(&block_address) {
                continue;
            }

            let block_bytes = memory.get_bytes(block_address, DEFAULT_TRANSLATION_BLOCK_BYTES);
            if block_bytes.is_empty() {
                let mut control_flow_graph = ControlFlowGraph::new();
                let block_index = control_flow_graph.new_block()?.index();
                control_flow_graph.set_entry(block_index)?;
                control_flow_graph.set_exit(block_index)?;
                translation_results.insert(
                    block_address,
                    BlockTranslationResult::new(
                        vec![(block_address, control_flow_graph)],
                        block_address,
                        0,
                        Vec::new(),
                    ),
                );
                continue;
            }

            // translate this block
            let block_translation_result =
                self.translate_block(&block_bytes, block_address, options)?;

            // enqueue all successors
            for successor in block_translation_result.successors().iter() {
                if !translation_queue.contains(&successor.0) {
                    translation_queue.push_back(successor.0);
                }
            }

            translation_results.insert(block_address, block_translation_result);
        }

        // We now insert all of these blocks into a new control flow graph,
        // keeping track of their new entry and exit indices.

        // A mapping of instruction address to entry/exit vertex indices
        let mut instruction_indices: BTreeMap<u64, (usize, usize)> = BTreeMap::new();

        // A mapping of block address to entry/exit vertex indices;
        let mut block_indices: BTreeMap<u64, (usize, usize)> = BTreeMap::new();

        let mut control_flow_graph = ControlFlowGraph::new();
        for result in &translation_results {
            let block_translation_result = result.1;
            let mut block_entry = 0;
            let mut block_exit = 0;
            let mut previous_exit = None;
            for &(address, ref instruction_graph) in block_translation_result.instructions().iter()
            {
                // Have we already inserted this instruction?
                let (entry, exit) = if instruction_indices.get(&address).is_some() {
                    instruction_indices[&address]
                } else {
                    let (entry, exit) = control_flow_graph.insert(instruction_graph)?;
                    instruction_indices.insert(address, (entry, exit));
                    (entry, exit)
                };
                // If this is not our first instruction through this block.
                if let Some(previous_exit) = previous_exit {
                    // If an edge from the previous block to this block doesn't
                    // exist
                    if control_flow_graph.edge(previous_exit, entry).is_err() {
                        // Create an edge from the previous block to this block.
                        control_flow_graph.unconditional_edge(previous_exit, entry)?;
                    }
                }
                // Our first instruction through this block
                else {
                    block_entry = entry;
                }
                block_exit = exit;
                previous_exit = Some(exit);
            }
            block_indices.insert(*result.0, (block_entry, block_exit));
        }

        // Insert the edges

        // Start with edges for our manual edges
        for manual_edge in options.manual_edges() {
            let (_, edge_head) = block_indices[&manual_edge.head_address()];
            let (edge_tail, _) = block_indices[&manual_edge.tail_address()];

            if control_flow_graph.edge(edge_head, edge_tail).is_ok() {
                continue;
            }

            if let Some(condition) = manual_edge.condition() {
                control_flow_graph.conditional_edge(edge_head, edge_tail, condition.clone())?;
            } else {
                control_flow_graph.unconditional_edge(edge_head, edge_tail)?;
            }
        }

        // For every block translation result
        for (address, block_translation_result) in translation_results {
            // Get the exit index for the last/tail vertex in this block
            let (_, block_exit) = block_indices[&address];
            // For every successor in the block translation result (this is an
            // (address, condition) tuple)
            for (successor_address, successor_condition) in
                block_translation_result.successors().iter()
            {
                // get the entry index for the first/head block in the successor
                let (block_entry, _) = block_indices[successor_address];
                // check for duplicate edges
                if control_flow_graph.edge(block_exit, block_entry).is_ok() {
                    continue;
                }
                match successor_condition {
                    Some(ref condition) => control_flow_graph.conditional_edge(
                        block_exit,
                        block_entry,
                        condition.clone(),
                    )?,
                    None => control_flow_graph.unconditional_edge(block_exit, block_entry)?,
                }
            }
        }

        // One block is the start of our control_flow_graph
        control_flow_graph.set_entry(block_indices[&function_address].0)?;

        // merge for the user
        control_flow_graph.merge()?;

        Ok(Function::new(function_address, control_flow_graph))
    }
}
<|endoftext|>


<commit_before>//! An example of how to build multi-key transactions on top of
//! sled's single-key atomicity guarantees.
//!
//! The basic idea is to hash all of the keys, and acquire locks
//! for each one in a big array of RwLock's. If we fail to acquire
//! a lock in a non-blocking way, we try to acquire a global serializing
//! mutex, then take out blocking locks on the required keys. The
//! multiple locks are to avoid deadlock. Maybe it's just really buggy!
//! The global mutex does not prevent other thgets from claiming sharded
//! mutexes in a non-blocking way, so it shouldn't get hit all that often.
//! But this isn't measured, so maybe it happens all the time!
//! Computers, right?

extern crate sled;

mod crc16;

use std::sync::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};

use sled::{Config, DbResult, Error, Tree};

macro_rules! rep_no_copy {
    ($e:expr; $n:expr) => {
        {
            let mut v = Vec::with_capacity($n);
            for _ in 0..$n {
                v.push($e);
            }
            v
        }
    };
}

type TxId = u64;
type Key = Vec<u8>;
type Value = Vec<u8>;
type PredicateFn = fn(&Key, &Option<Value>) -> bool;

#[derive(Debug, PartialEq)]
enum TxRet {
    Committed(Vec<(Key, Option<Value>)>),
    PredicateFailure,
}

unsafe impl Send for TxRet {}

struct TxDb {
    locks: Vec<RwLock<()>>,
    tree: Tree,
    blocker: Mutex<()>,
}

unsafe impl Send for TxDb {}
unsafe impl Sync for TxDb {}

struct Tx<'a> {
    predicates: Vec<Predicate>,
    sets: Vec<Write>,
    gets: Vec<Read>,
    deletes: Vec<Delete>,
    db: &'a TxDb,
}

unsafe impl<'a> Send for Tx<'a> {}

struct Predicate(Key, Box<PredicateFn>);
struct Write(Key, Value);
struct Read(Key);
struct Delete(Key);

fn split_txid_from_v(v: Value) -> DbResult<(TxId, Value), ()> {
    if v.len() < 8 {
        return Err(Error::ReportableBug("get bad txid".to_owned()));
    }
    let mut first_8 = [0; 8];
    first_8.copy_from_slice(&*v);
    let txid = unsafe { std::mem::transmute(first_8) };
    Ok((txid, v.into_iter().skip(8).collect()))
}

impl TxDb {
    fn start(config: Config) -> DbResult<TxDb, ()> {
        Ok(TxDb {
            // pluggable
            tree: Tree::start(config)?,
            blocker: Mutex::new(()),
            // 65k locks! webscale!!!
            locks: rep_no_copy!(RwLock::new(()); 1 << 16),
        })
    }

    fn tx<'a>(&'a self) -> Tx<'a> {
        Tx {
            predicates: vec![],
            gets: vec![],
            sets: vec![],
            deletes: vec![],
            db: &self,
        }
    }

    fn lock_keys(
        &self,
        get_hashes: &[u16],
        set_hashes: &[u16],
    ) -> (Vec<RwLockReadGuard<()>>, Vec<RwLockWriteGuard<()>>) {
        let mut get_locks = vec![];
        for get_idx in get_hashes {
            if let Ok(get_guard) = self.locks[*get_idx as usize].try_read() {
                get_locks.push(get_guard);
            } else {
                drop(get_locks);
                return self.pessimistic_lock_keys(get_hashes, set_hashes);
            }
        }

        let mut set_locks = vec![];
        for set_idx in set_hashes {
            if let Ok(set_guard) = self.locks[*set_idx as usize].try_write() {
                set_locks.push(set_guard);
            } else {
                drop(set_locks);
                let (_r, w) = self.pessimistic_lock_keys(&[], set_hashes);
                set_locks = w;
                break;
            }
        }

        (get_locks, set_locks)
    }

    fn pessimistic_lock_keys(
        &self,
        get_hashes: &[u16],
        set_hashes: &[u16],
    ) -> (Vec<RwLockReadGuard<()>>, Vec<RwLockWriteGuard<()>>) {
        let _global_mu = self.blocker.lock().unwrap();

        let mut get_locks = vec![];
        for get_idx in get_hashes {
            let get_guard = self.locks[*get_idx as usize].read().unwrap();
            get_locks.push(get_guard);
        }

        let mut set_locks = vec![];
        for set_idx in set_hashes {
            let set_guard = self.locks[*set_idx as usize].write().unwrap();
            set_locks.push(set_guard);
        }

        (get_locks, set_locks)
    }
}

impl<'a> Tx<'a> {
    fn predicate(&mut self, k: Key, p: PredicateFn) {
        self.predicates.push(Predicate(k, Box::new(p)));
    }

    fn get(&mut self, k: Key) {
        self.gets.push(Read(k));
    }

    fn set(&mut self, k: Key, v: Value) {
        self.sets.push(Write(k, v));
    }

    fn del(&mut self, k: Key) {
        self.deletes.push(Delete(k));
    }

    fn execute(self) -> DbResult<TxRet, ()> {
        // claim locks
        let mut get_keys = vec![];
        let mut set_keys = vec![];

        for &Predicate(ref k, _) in &self.predicates {
            get_keys.push(k);
        }

        for &Read(ref k) in &self.gets {
            get_keys.push(k);
        }

        for &Write(ref k, _) in &self.sets {
            set_keys.push(k);
        }

        for &Delete(ref k) in &self.deletes {
            set_keys.push(k);
        }

        let mut set_hashes: Vec<u16> =
            set_keys.into_iter().map(|k| crc16::crc16(k)).collect();
        set_hashes.sort();
        set_hashes.dedup();

        let mut get_hashes: Vec<u16> = get_keys
            .into_iter()
            .map(|k| crc16::crc16(k))
            .filter(|hash| !set_hashes.contains(hash))
            .collect();
        get_hashes.sort();
        get_hashes.dedup();

        let (get_locks, _set_locks) =
            self.db.lock_keys(&*get_hashes, &*set_hashes);

        // perform predicate matches
        for &Predicate(ref k, ref p) in &self.predicates {
            let current = self.db.tree.get(k)?;
            if !p(&k, &current) {
                return Ok(TxRet::PredicateFailure);
            }
        }

        // perform sets
        for &Write(ref k, ref v) in &self.sets {
            self.db.tree.set(k.clone(), v.clone())?;
        }

        // perform deletes
        for &Delete(ref k) in &self.deletes {
            self.db.tree.del(k)?;
        }

        // perform gets
        let mut ret_gets = vec![];
        for &Read(ref k) in &self.gets {
            ret_gets.push((k.clone(), self.db.tree.get(k)?));
        }

        Ok(TxRet::Committed(ret_gets))
    }
}

#[test]
fn it_works() {
    let conf = sled::ConfigBuilder::new().temporary(true).build();
    let txdb = TxDb::start(conf).unwrap();

    let mut tx = txdb.tx();
    tx.set(b"cats".to_vec(), b"meow".to_vec());
    tx.set(b"dogs".to_vec(), b"woof".to_vec());
    assert_eq!(tx.execute(), Ok(TxRet::Committed(vec![])));

    let mut tx = txdb.tx();
    tx.predicate(b"cats".to_vec(), |k, v| *v == Some(b"meow".to_vec()));
    tx.predicate(b"dogs".to_vec(), |k, v| *v == Some(b"woof".to_vec()));
    tx.set(b"cats".to_vec(), b"woof".to_vec());
    tx.set(b"dogs".to_vec(), b"meow".to_vec());
    tx.get(b"dogs".to_vec());
    assert_eq!(
        tx.execute(),
        Ok(TxRet::Committed(vec![(b"dogs".to_vec(), Some(b"meow".to_vec()))]))
    );

    let mut tx = txdb.tx();
    tx.predicate(b"cats".to_vec(), |k, v| *v == Some(b"meow".to_vec()));
    assert_eq!(tx.execute(), Ok(TxRet::PredicateFailure));
}
<commit_msg>fix warnings in example<commit_after>//! An example of how to build multi-key transactions on top of
//! sled's single-key atomicity guarantees.
//!
//! The basic idea is to hash all of the keys, and acquire locks
//! for each one in a big array of RwLock's. If we fail to acquire
//! a lock in a non-blocking way, we try to acquire a global serializing
//! mutex, then take out blocking locks on the required keys. The
//! multiple locks are to avoid deadlock. Maybe it's just really buggy!
//! The global mutex does not prevent other thgets from claiming sharded
//! mutexes in a non-blocking way, so it shouldn't get hit all that often.
//! But this isn't measured, so maybe it happens all the time!
//! Computers, right?

#![allow(dead_code)]

extern crate sled;

mod crc16;

use std::sync::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};

use sled::{Config, DbResult, Error, Tree};

macro_rules! rep_no_copy {
    ($e:expr; $n:expr) => {
        {
            let mut v = Vec::with_capacity($n);
            for _ in 0..$n {
                v.push($e);
            }
            v
        }
    };
}

type TxId = u64;
type Key = Vec<u8>;
type Value = Vec<u8>;
type PredicateFn = fn(&Key, &Option<Value>) -> bool;

#[derive(Debug, PartialEq)]
enum TxRet {
    Committed(Vec<(Key, Option<Value>)>),
    PredicateFailure,
}

unsafe impl Send for TxRet {}

struct TxDb {
    locks: Vec<RwLock<()>>,
    tree: Tree,
    blocker: Mutex<()>,
}

unsafe impl Send for TxDb {}
unsafe impl Sync for TxDb {}

struct Tx<'a> {
    predicates: Vec<Predicate>,
    sets: Vec<Write>,
    gets: Vec<Read>,
    deletes: Vec<Delete>,
    db: &'a TxDb,
}

unsafe impl<'a> Send for Tx<'a> {}

struct Predicate(Key, Box<PredicateFn>);
struct Write(Key, Value);
struct Read(Key);
struct Delete(Key);

fn split_txid_from_v(v: Value) -> DbResult<(TxId, Value), ()> {
    if v.len() < 8 {
        return Err(Error::ReportableBug("get bad txid".to_owned()));
    }
    let mut first_8 = [0; 8];
    first_8.copy_from_slice(&*v);
    let txid = unsafe { std::mem::transmute(first_8) };
    Ok((txid, v.into_iter().skip(8).collect()))
}

impl TxDb {
    fn start(config: Config) -> DbResult<TxDb, ()> {
        Ok(TxDb {
            // pluggable
            tree: Tree::start(config)?,
            blocker: Mutex::new(()),
            // 65k locks! webscale!!!
            locks: rep_no_copy!(RwLock::new(()); 1 << 16),
        })
    }

    fn tx<'a>(&'a self) -> Tx<'a> {
        Tx {
            predicates: vec![],
            gets: vec![],
            sets: vec![],
            deletes: vec![],
            db: &self,
        }
    }

    fn lock_keys(
        &self,
        get_hashes: &[u16],
        set_hashes: &[u16],
    ) -> (Vec<RwLockReadGuard<()>>, Vec<RwLockWriteGuard<()>>) {
        let mut get_locks = vec![];
        for get_idx in get_hashes {
            if let Ok(get_guard) = self.locks[*get_idx as usize].try_read() {
                get_locks.push(get_guard);
            } else {
                drop(get_locks);
                return self.pessimistic_lock_keys(get_hashes, set_hashes);
            }
        }

        let mut set_locks = vec![];
        for set_idx in set_hashes {
            if let Ok(set_guard) = self.locks[*set_idx as usize].try_write() {
                set_locks.push(set_guard);
            } else {
                drop(set_locks);
                let (_r, w) = self.pessimistic_lock_keys(&[], set_hashes);
                set_locks = w;
                break;
            }
        }

        (get_locks, set_locks)
    }

    fn pessimistic_lock_keys(
        &self,
        get_hashes: &[u16],
        set_hashes: &[u16],
    ) -> (Vec<RwLockReadGuard<()>>, Vec<RwLockWriteGuard<()>>) {
        let _global_mu = self.blocker.lock().unwrap();

        let mut get_locks = vec![];
        for get_idx in get_hashes {
            let get_guard = self.locks[*get_idx as usize].read().unwrap();
            get_locks.push(get_guard);
        }

        let mut set_locks = vec![];
        for set_idx in set_hashes {
            let set_guard = self.locks[*set_idx as usize].write().unwrap();
            set_locks.push(set_guard);
        }

        (get_locks, set_locks)
    }
}

impl<'a> Tx<'a> {
    fn predicate(&mut self, k: Key, p: PredicateFn) {
        self.predicates.push(Predicate(k, Box::new(p)));
    }

    fn get(&mut self, k: Key) {
        self.gets.push(Read(k));
    }

    fn set(&mut self, k: Key, v: Value) {
        self.sets.push(Write(k, v));
    }

    fn del(&mut self, k: Key) {
        self.deletes.push(Delete(k));
    }

    fn execute(self) -> DbResult<TxRet, ()> {
        // claim locks
        let mut get_keys = vec![];
        let mut set_keys = vec![];

        for &Predicate(ref k, _) in &self.predicates {
            get_keys.push(k);
        }

        for &Read(ref k) in &self.gets {
            get_keys.push(k);
        }

        for &Write(ref k, _) in &self.sets {
            set_keys.push(k);
        }

        for &Delete(ref k) in &self.deletes {
            set_keys.push(k);
        }

        let mut set_hashes: Vec<u16> =
            set_keys.into_iter().map(|k| crc16::crc16(k)).collect();
        set_hashes.sort();
        set_hashes.dedup();

        let mut get_hashes: Vec<u16> = get_keys
            .into_iter()
            .map(|k| crc16::crc16(k))
            .filter(|hash| !set_hashes.contains(hash))
            .collect();
        get_hashes.sort();
        get_hashes.dedup();

        let _locks = self.db.lock_keys(&*get_hashes, &*set_hashes);

        // perform predicate matches
        for &Predicate(ref k, ref p) in &self.predicates {
            let current = self.db.tree.get(k)?;
            if !p(&k, &current) {
                return Ok(TxRet::PredicateFailure);
            }
        }

        // perform sets
        for &Write(ref k, ref v) in &self.sets {
            self.db.tree.set(k.clone(), v.clone())?;
        }

        // perform deletes
        for &Delete(ref k) in &self.deletes {
            self.db.tree.del(k)?;
        }

        // perform gets
        let mut ret_gets = vec![];
        for &Read(ref k) in &self.gets {
            ret_gets.push((k.clone(), self.db.tree.get(k)?));
        }

        Ok(TxRet::Committed(ret_gets))
    }
}

#[test]
fn it_works() {
    let conf = sled::ConfigBuilder::new().temporary(true).build();
    let txdb = TxDb::start(conf).unwrap();

    let mut tx = txdb.tx();
    tx.set(b"cats".to_vec(), b"meow".to_vec());
    tx.set(b"dogs".to_vec(), b"woof".to_vec());
    assert_eq!(tx.execute(), Ok(TxRet::Committed(vec![])));

    let mut tx = txdb.tx();
    tx.predicate(b"cats".to_vec(), |_k, v| *v == Some(b"meow".to_vec()));
    tx.predicate(b"dogs".to_vec(), |_k, v| *v == Some(b"woof".to_vec()));
    tx.set(b"cats".to_vec(), b"woof".to_vec());
    tx.set(b"dogs".to_vec(), b"meow".to_vec());
    tx.get(b"dogs".to_vec());
    assert_eq!(
        tx.execute(),
        Ok(TxRet::Committed(vec![(b"dogs".to_vec(), Some(b"meow".to_vec()))]))
    );

    let mut tx = txdb.tx();
    tx.predicate(b"cats".to_vec(), |_k, v| *v == Some(b"meow".to_vec()));
    assert_eq!(tx.execute(), Ok(TxRet::PredicateFailure));
}
<|endoftext|>


<commit_before>pub(crate) mod tags;

mod highlights;
mod injector;

mod highlight;
mod format;
mod macro_rules;
mod inject;

mod html;
#[cfg(test)]
mod tests;

use hir::{Name, Semantics};
use ide_db::{RootDatabase, SymbolKind};
use rustc_hash::FxHashMap;
use syntax::{
    ast::{self, HasFormatSpecifier},
    AstNode, AstToken, Direction, NodeOrToken,
    SyntaxKind::*,
    SyntaxNode, TextRange, WalkEvent, T,
};

use crate::{
    syntax_highlighting::{
        format::highlight_format_string, highlights::Highlights,
        macro_rules::MacroRulesHighlighter, tags::Highlight,
    },
    FileId, HlMod, HlTag,
};

pub(crate) use html::highlight_as_html;

#[derive(Debug, Clone, Copy)]
pub struct HlRange {
    pub range: TextRange,
    pub highlight: Highlight,
    pub binding_hash: Option<u64>,
}

// Feature: Semantic Syntax Highlighting
//
// rust-analyzer highlights the code semantically.
// For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait.
// rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token.
// It's up to the client to map those to specific colors.
//
// The general rule is that a reference to an entity gets colored the same way as the entity itself.
// We also give special modifier for `mut` and `&mut` local variables.
pub(crate) fn highlight(
    db: &RootDatabase,
    file_id: FileId,
    range_to_highlight: Option<TextRange>,
    syntactic_name_ref_highlighting: bool,
) -> Vec<HlRange> {
    let _p = profile::span("highlight");
    let sema = Semantics::new(db);

    // Determine the root based on the given range.
    let (root, range_to_highlight) = {
        let source_file = sema.parse(file_id);
        match range_to_highlight {
            Some(range) => {
                let node = match source_file.syntax().covering_element(range) {
                    NodeOrToken::Node(it) => it,
                    NodeOrToken::Token(it) => it.parent(),
                };
                (node, range)
            }
            None => (source_file.syntax().clone(), source_file.syntax().text_range()),
        }
    };

    let mut hl = highlights::Highlights::new(range_to_highlight);
    traverse(&mut hl, &sema, &root, range_to_highlight, syntactic_name_ref_highlighting);
    hl.to_vec()
}

fn traverse(
    hl: &mut Highlights,
    sema: &Semantics<RootDatabase>,
    root: &SyntaxNode,
    range_to_highlight: TextRange,
    syntactic_name_ref_highlighting: bool,
) {
    let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();

    let mut current_macro_call: Option<ast::MacroCall> = None;
    let mut current_macro_rules: Option<ast::MacroRules> = None;
    let mut macro_rules_highlighter = MacroRulesHighlighter::default();
    let mut inside_attribute = false;

    // Walk all nodes, keeping track of whether we are inside a macro or not.
    // If in macro, expand it first and highlight the expanded code.
    for event in root.preorder_with_tokens() {
        let event_range = match &event {
            WalkEvent::Enter(it) | WalkEvent::Leave(it) => it.text_range(),
        };

        // Element outside of the viewport, no need to highlight
        if range_to_highlight.intersect(event_range).is_none() {
            continue;
        }

        // Track "inside macro" state
        match event.clone().map(|it| it.into_node().and_then(ast::MacroCall::cast)) {
            WalkEvent::Enter(Some(mc)) => {
                if let Some(range) = macro_call_range(&mc) {
                    hl.add(HlRange {
                        range,
                        highlight: HlTag::Symbol(SymbolKind::Macro).into(),
                        binding_hash: None,
                    });
                }
                current_macro_call = Some(mc.clone());
                continue;
            }
            WalkEvent::Leave(Some(mc)) => {
                assert_eq!(current_macro_call, Some(mc));
                current_macro_call = None;
            }
            _ => (),
        }

        match event.clone().map(|it| it.into_node().and_then(ast::MacroRules::cast)) {
            WalkEvent::Enter(Some(mac)) => {
                macro_rules_highlighter.init();
                current_macro_rules = Some(mac);
                continue;
            }
            WalkEvent::Leave(Some(mac)) => {
                assert_eq!(current_macro_rules, Some(mac));
                current_macro_rules = None;
                macro_rules_highlighter = MacroRulesHighlighter::default();
            }
            _ => (),
        }
        match &event {
            WalkEvent::Enter(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => {
                inside_attribute = true
            }
            WalkEvent::Leave(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => {
                inside_attribute = false
            }
            _ => (),
        }

        let element = match event {
            WalkEvent::Enter(it) => it,
            WalkEvent::Leave(it) => {
                if let Some(node) = it.as_node() {
                    inject::doc_comment(hl, node);
                }
                continue;
            }
        };

        let range = element.text_range();

        if current_macro_rules.is_some() {
            if let Some(tok) = element.as_token() {
                macro_rules_highlighter.advance(tok);
            }
        }

        let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT {
            // Inside a macro -- expand it first
            let token = match element.clone().into_token() {
                Some(it) if it.parent().kind() == TOKEN_TREE => it,
                _ => continue,
            };
            let token = sema.descend_into_macros(token.clone());
            let parent = token.parent();

            // We only care Name and Name_ref
            match (token.kind(), parent.kind()) {
                (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(),
                _ => token.into(),
            }
        } else {
            element.clone()
        };

        if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) {
            if token.is_raw() {
                let expanded = element_to_highlight.as_token().unwrap().clone();
                if inject::ra_fixture(hl, &sema, token, expanded).is_some() {
                    continue;
                }
            }
        }

        if let Some(_) = macro_rules_highlighter.highlight(element_to_highlight.clone()) {
            continue;
        }

        if let Some((mut highlight, binding_hash)) = highlight::element(
            &sema,
            &mut bindings_shadow_count,
            syntactic_name_ref_highlighting,
            element_to_highlight.clone(),
        ) {
            if inside_attribute {
                highlight = highlight | HlMod::Attribute;
            }

            hl.add(HlRange { range, highlight, binding_hash });
        }

        if let Some(string) = element_to_highlight.as_token().cloned().and_then(ast::String::cast) {
            highlight_format_string(hl, &string, range);
            // Highlight escape sequences
            if let Some(char_ranges) = string.char_ranges() {
                for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) {
                    if string.text()[piece_range.start().into()..].starts_with('\\') {
                        hl.add(HlRange {
                            range: piece_range + range.start(),
                            highlight: HlTag::EscapeSequence.into(),
                            binding_hash: None,
                        });
                    }
                }
            }
        }
    }
}

fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> {
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    let range_start = name_ref.syntax().text_range().start();
    let mut range_end = name_ref.syntax().text_range().end();
    for sibling in path.syntax().siblings_with_tokens(Direction::Next) {
        match sibling.kind() {
            T![!] | IDENT => range_end = sibling.text_range().end(),
            _ => (),
        }
    }

    Some(TextRange::new(range_start, range_end))
}
<commit_msg>Increase Highlights highlight range to covering element<commit_after>pub(crate) mod tags;

mod highlights;
mod injector;

mod highlight;
mod format;
mod macro_rules;
mod inject;

mod html;
#[cfg(test)]
mod tests;

use hir::{Name, Semantics};
use ide_db::{RootDatabase, SymbolKind};
use rustc_hash::FxHashMap;
use syntax::{
    ast::{self, HasFormatSpecifier},
    AstNode, AstToken, Direction, NodeOrToken,
    SyntaxKind::*,
    SyntaxNode, TextRange, WalkEvent, T,
};

use crate::{
    syntax_highlighting::{
        format::highlight_format_string, highlights::Highlights,
        macro_rules::MacroRulesHighlighter, tags::Highlight,
    },
    FileId, HlMod, HlTag,
};

pub(crate) use html::highlight_as_html;

#[derive(Debug, Clone, Copy)]
pub struct HlRange {
    pub range: TextRange,
    pub highlight: Highlight,
    pub binding_hash: Option<u64>,
}

// Feature: Semantic Syntax Highlighting
//
// rust-analyzer highlights the code semantically.
// For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait.
// rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token.
// It's up to the client to map those to specific colors.
//
// The general rule is that a reference to an entity gets colored the same way as the entity itself.
// We also give special modifier for `mut` and `&mut` local variables.
pub(crate) fn highlight(
    db: &RootDatabase,
    file_id: FileId,
    range_to_highlight: Option<TextRange>,
    syntactic_name_ref_highlighting: bool,
) -> Vec<HlRange> {
    let _p = profile::span("highlight");
    let sema = Semantics::new(db);

    // Determine the root based on the given range.
    let (root, range_to_highlight) = {
        let source_file = sema.parse(file_id);
        match range_to_highlight {
            Some(range) => {
                let node = match source_file.syntax().covering_element(range) {
                    NodeOrToken::Node(it) => it,
                    NodeOrToken::Token(it) => it.parent(),
                };
                (node, range)
            }
            None => (source_file.syntax().clone(), source_file.syntax().text_range()),
        }
    };

    let mut hl = highlights::Highlights::new(root.text_range());
    traverse(&mut hl, &sema, &root, range_to_highlight, syntactic_name_ref_highlighting);
    hl.to_vec()
}

fn traverse(
    hl: &mut Highlights,
    sema: &Semantics<RootDatabase>,
    root: &SyntaxNode,
    range_to_highlight: TextRange,
    syntactic_name_ref_highlighting: bool,
) {
    let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();

    let mut current_macro_call: Option<ast::MacroCall> = None;
    let mut current_macro_rules: Option<ast::MacroRules> = None;
    let mut macro_rules_highlighter = MacroRulesHighlighter::default();
    let mut inside_attribute = false;

    // Walk all nodes, keeping track of whether we are inside a macro or not.
    // If in macro, expand it first and highlight the expanded code.
    for event in root.preorder_with_tokens() {
        let event_range = match &event {
            WalkEvent::Enter(it) | WalkEvent::Leave(it) => it.text_range(),
        };

        // Element outside of the viewport, no need to highlight
        if range_to_highlight.intersect(event_range).is_none() {
            continue;
        }

        // Track "inside macro" state
        match event.clone().map(|it| it.into_node().and_then(ast::MacroCall::cast)) {
            WalkEvent::Enter(Some(mc)) => {
                if let Some(range) = macro_call_range(&mc) {
                    hl.add(HlRange {
                        range,
                        highlight: HlTag::Symbol(SymbolKind::Macro).into(),
                        binding_hash: None,
                    });
                }
                current_macro_call = Some(mc.clone());
                continue;
            }
            WalkEvent::Leave(Some(mc)) => {
                assert_eq!(current_macro_call, Some(mc));
                current_macro_call = None;
            }
            _ => (),
        }

        match event.clone().map(|it| it.into_node().and_then(ast::MacroRules::cast)) {
            WalkEvent::Enter(Some(mac)) => {
                macro_rules_highlighter.init();
                current_macro_rules = Some(mac);
                continue;
            }
            WalkEvent::Leave(Some(mac)) => {
                assert_eq!(current_macro_rules, Some(mac));
                current_macro_rules = None;
                macro_rules_highlighter = MacroRulesHighlighter::default();
            }
            _ => (),
        }
        match &event {
            WalkEvent::Enter(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => {
                inside_attribute = true
            }
            WalkEvent::Leave(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => {
                inside_attribute = false
            }
            _ => (),
        }

        let element = match event {
            WalkEvent::Enter(it) => it,
            WalkEvent::Leave(it) => {
                if let Some(node) = it.as_node() {
                    inject::doc_comment(hl, node);
                }
                continue;
            }
        };

        let range = element.text_range();

        if current_macro_rules.is_some() {
            if let Some(tok) = element.as_token() {
                macro_rules_highlighter.advance(tok);
            }
        }

        let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT {
            // Inside a macro -- expand it first
            let token = match element.clone().into_token() {
                Some(it) if it.parent().kind() == TOKEN_TREE => it,
                _ => continue,
            };
            let token = sema.descend_into_macros(token.clone());
            let parent = token.parent();

            // We only care Name and Name_ref
            match (token.kind(), parent.kind()) {
                (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(),
                _ => token.into(),
            }
        } else {
            element.clone()
        };

        if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) {
            if token.is_raw() {
                let expanded = element_to_highlight.as_token().unwrap().clone();
                if inject::ra_fixture(hl, &sema, token, expanded).is_some() {
                    continue;
                }
            }
        }

        if let Some(_) = macro_rules_highlighter.highlight(element_to_highlight.clone()) {
            continue;
        }

        if let Some((mut highlight, binding_hash)) = highlight::element(
            &sema,
            &mut bindings_shadow_count,
            syntactic_name_ref_highlighting,
            element_to_highlight.clone(),
        ) {
            if inside_attribute {
                highlight = highlight | HlMod::Attribute;
            }

            hl.add(HlRange { range, highlight, binding_hash });
        }

        if let Some(string) = element_to_highlight.as_token().cloned().and_then(ast::String::cast) {
            highlight_format_string(hl, &string, range);
            // Highlight escape sequences
            if let Some(char_ranges) = string.char_ranges() {
                for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) {
                    if string.text()[piece_range.start().into()..].starts_with('\\') {
                        hl.add(HlRange {
                            range: piece_range + range.start(),
                            highlight: HlTag::EscapeSequence.into(),
                            binding_hash: None,
                        });
                    }
                }
            }
        }
    }
}

fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> {
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    let range_start = name_ref.syntax().text_range().start();
    let mut range_end = name_ref.syntax().text_range().end();
    for sibling in path.syntax().siblings_with_tokens(Direction::Next) {
        match sibling.kind() {
            T![!] | IDENT => range_end = sibling.text_range().end(),
            _ => (),
        }
    }

    Some(TextRange::new(range_start, range_end))
}
<|endoftext|>


<commit_before>// Copyright 2012 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.

// Tests that type assignability is used to search for instances when
// making method calls, but only if there aren't any matches without
// it.

trait iterable<A> {
    fn iterate(&self, blk: |x: &A| -> bool) -> bool;
}

impl<'a,A> iterable<A> for &'a [A] {
    fn iterate(&self, f: |x: &A| -> bool) -> bool {
        self.iter().all(f)
    }
}

impl<A> iterable<A> for Vec<A> {
    fn iterate(&self, f: |x: &A| -> bool) -> bool {
        self.iter().all(f)
    }
}

fn length<A, T: iterable<A>>(x: T) -> uint {
    let mut len = 0;
    x.iterate(|_y| {
        len += 1;
        true
    });
    return len;
}

pub fn main() {
    let x: Vec<int> = vec!(0,1,2,3);
    // Call a method
    x.iterate(|y| { assert!(x[*y as uint] == *y); true });
    // Call a parameterized function
    assert_eq!(length(x.clone()), x.len());
    // Call a parameterized function, with type arguments that require
    // a borrow
    assert_eq!(length::<int, &[int]>(x.as_slice()), x.len());

    // Now try it with a type that *needs* to be borrowed
    let z = [0,1,2,3];
    // Call a method
    z.iterate(|y| { assert!(z[*y as uint] == *y); true });
    // Call a parameterized function
    assert_eq!(length::<int, &[int]>(&z), z.len());
}
<commit_msg>Fix run-pass tests<commit_after>// Copyright 2012 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.

// Tests that type assignability is used to search for instances when
// making method calls, but only if there aren't any matches without
// it.

#![feature(unboxed_closures)]

trait iterable<A> {
    fn iterate<F>(&self, blk: F) -> bool where F: FnMut(&A) -> bool;
}

impl<'a,A> iterable<A> for &'a [A] {
    fn iterate<F>(&self, f: F) -> bool where F: FnMut(&A) -> bool {
        self.iter().all(f)
    }
}

impl<A> iterable<A> for Vec<A> {
    fn iterate<F>(&self, f: F) -> bool where F: FnMut(&A) -> bool {
        self.iter().all(f)
    }
}

fn length<A, T: iterable<A>>(x: T) -> uint {
    let mut len = 0;
    x.iterate(|_y| {
        len += 1;
        true
    });
    return len;
}

pub fn main() {
    let x: Vec<int> = vec!(0,1,2,3);
    // Call a method
    x.iterate(|y| { assert!(x[*y as uint] == *y); true });
    // Call a parameterized function
    assert_eq!(length(x.clone()), x.len());
    // Call a parameterized function, with type arguments that require
    // a borrow
    assert_eq!(length::<int, &[int]>(x.as_slice()), x.len());

    // Now try it with a type that *needs* to be borrowed
    let z = [0,1,2,3];
    // Call a method
    z.iterate(|y| { assert!(z[*y as uint] == *y); true });
    // Call a parameterized function
    assert_eq!(length::<int, &[int]>(&z), z.len());
}
<|endoftext|>


<commit_before>//! Parse tree -> AST conversion

use super::*;
use parser::parsetree;
use parser::span::Spanned;

use lea_core::constant::Const;

impl<'a> From<parsetree::Function<'a>> for Function<'a> {
    fn from(func: parsetree::Function<'a>) -> Self {
        Function {
            params: func.params,
            varargs: func.varargs,
            body: func.body.into(),
            locals: Default::default(),
            upvalues: Default::default(),
        }
    }
}

impl<'a> From<parsetree::Block<'a>> for Block<'a> {
    fn from(block: parsetree::Block<'a>) -> Self {
        Block {
            span: block.span,
            stmts: conv_vec(block.stmts),
            localmap: Default::default(),
        }
    }
}

impl<'a> From<parsetree::_Variable<'a>> for _Variable<'a> {
    fn from(var: parsetree::_Variable<'a>) -> Self {
        match var {
            parsetree::VNamed(name) => {
                VNamed(name)
            }
            parsetree::VIndex(var, index) => {
                VIndex(Box::new(spanned_into(*var)), match index {
                    parsetree::VarIndex::DotIndex(name) => {
                        Box::new(Spanned::new(name.span,
                            ELit(Const::Str(name.value.to_owned()))))
                    }
                    parsetree::VarIndex::ExprIndex(expr) => {
                        Box::new(spanned_into(*expr))
                    }
                })
            }
        }
    }
}

impl<'a> From<parsetree::_Expr<'a>> for _Expr<'a> {
    fn from(expr: parsetree::_Expr<'a>) -> Self {
        match expr {
            parsetree::ELit(lit) => ELit(lit),
            parsetree::EBinOp(lhs, op, rhs) =>
                EBinOp(Box::new(spanned_into(*lhs)), op, Box::new(spanned_into(*rhs))),
            parsetree::EUnOp(op, expr) => EUnOp(op, Box::new(spanned_into(*expr))),
            parsetree::EBraced(expr) => expr.value.into(),

            parsetree::EVar(var) => EVar(spanned_into(var)),

            parsetree::ECall(call) => ECall(match call {
                parsetree::SimpleCall(callee, args) => {
                    SimpleCall(Box::new(spanned_into(*callee)), conv_args(args))
                }
                parsetree::MethodCall(obj, name, args) => {
                    MethodCall(Box::new(spanned_into(*obj)), name, conv_args(args))
                }
            }),

            parsetree::ETable(cons) => {
                ETable(conv_table(cons))
            }

            parsetree::EFunc(func) => EFunc(func.into()),
            parsetree::EArray(elems) => EArray(vec_into(elems)),
            parsetree::EVarArgs => EVarArgs,
        }
    }
}

/// Implemented for parse tree nodes. Converts them into `n` instances of `Target`.
trait Conv<'a> {
    type Target;

    fn conv<F>(self, push: F) where F: FnMut(Self::Target);
}

fn conv_table<'a>(cons: parsetree::TableCons<'a>) -> Vec<(Expr<'a>, Expr<'a>)> {
    let mut entries: Vec<(Expr<'a>, Expr<'a>)> = Vec::new();
    let mut i = 0;  // XXX 1?
    for entry in cons {
        entries.push(match entry {
            parsetree::TableEntry::Pair(key, value) => {
                (spanned_into(key), spanned_into(value))
            }
            parsetree::TableEntry::Elem(value) => {
                let idx = i;
                i += 1;
                (Spanned::new(value.span, ELit(Const::Int(idx))), spanned_into(value))
            }
        });
    }

    entries
}

fn conv_vec<'a, T: Conv<'a>>(v: Vec<Spanned<T>>) -> Vec<Spanned<T::Target>> {
    let mut res = Vec::new();
    for item in v.into_iter() {
        let span = item.span;
        item.value.conv(|i| res.push(Spanned::new(span, i)));
    }

    res
}

fn conv_args<'a>(args: parsetree::CallArgs<'a>) -> Vec<Expr<'a>> {
    let mut v = Vec::new();
    args.conv(|e| v.push(e));

    v
}

fn vec_into<'a, U, T: Into<U>>(v: Vec<Spanned<T>>) -> Vec<Spanned<U>> {
    v.into_iter().map(|item| spanned_into(item)).collect()
}

// Needed because Spanned<T> cannot impl Into. Not even specialization can help there.
fn spanned_into<U, T: Into<U>>(item: Spanned<T>) -> Spanned<U> {
    Spanned::new(item.span, item.value.into())
}

impl<'a> Conv<'a> for parsetree::_Stmt<'a> {
    type Target = _Stmt<'a>;

    fn conv<F>(self, mut push: F) where F: FnMut(Self::Target) {
        match self {
            parsetree::SDecl(names, exprs) => {
                push(_Stmt::SDecl(names, vec_into(exprs)));
            }

            parsetree::SAssign(vars, exprs) => {
                push(_Stmt::SAssign(vec_into(vars), vec_into(exprs)));
            }

            parsetree::SDo(block) => {
                push(_Stmt::SDo(block.into()));
            }

            parsetree::SBreak => {
                push(_Stmt::SBreak);
            }

            parsetree::SSemi => {}

            parsetree::SReturn(exprs) => {
                push(_Stmt::SReturn(vec_into(exprs)));
            }

            parsetree::SCall(call) => {
                push(_Stmt::SCall(match call {
                    parsetree::SimpleCall(callee, args) => {
                        SimpleCall(Box::new(spanned_into(*callee)), conv_args(args))
                    }
                    parsetree::MethodCall(obj, name, args) => {
                        MethodCall(Box::new(spanned_into(*obj)), name, conv_args(args))
                    }
                }));
            }

            parsetree::SFunc(var, func) => {
                push(_Stmt::SAssign(
                    vec![spanned_into(var)],
                    vec![Spanned::new(func.body.span, EFunc(func.into()))]
                ));
            }

            parsetree::SMethod(var, name, func) => {
                // var:name(args...)   =>   var.name = function(self, args...)
                let mut func: Function<'a> = func.into();
                func.params.insert(0, Spanned::new(name.span, "self"));

                push(_Stmt::SAssign(
                    vec![Spanned::new(var.span, VIndex(
                        Box::new(spanned_into(var)),
                        Box::new(Spanned::new(name.span, ELit(Const::Str(name.to_string()))))
                    ))],
                    vec![Spanned::new(func.body.span, EFunc(func))]
                ));
            }

            parsetree::SLFunc(local, func) => {
                push(_Stmt::SDecl(vec![local], vec![]));
                push(_Stmt::SAssign(
                    vec![Spanned::new(local.span, VNamed(local.value))],
                    vec![Spanned::new(func.body.span, EFunc(func.into()))]
                ));
            }

            parsetree::SIf { cond, body, elifs, el } => {
                // Build else block for this if statement
                // Start with "pure" else at the end
                let mut myelse: Option<Block> = el.map(|el| el.into());

                for elif in elifs {
                    let (cond, body) = elif.value;

                    myelse = Some(Block {
                        localmap: Default::default(),
                        span: elif.span,
                        stmts: vec![
                            Spanned::new(elif.span, SIf {
                                cond: spanned_into(cond),
                                body: body.into(),
                                el: myelse, // Old else block here
                            }),
                        ],
                    });
                }

                push(_Stmt::SIf {
                    cond: spanned_into(cond),
                    body: body.into(),
                    el: myelse,
                });
            }

            parsetree::SWhile { cond, body } => {
                push(_Stmt::SWhile {
                    cond: spanned_into(cond),
                    body: body.into(),
                });
            }

            parsetree::SRepeat { abort_on, body } => {
                push(_Stmt::SRepeat {
                    abort_on: spanned_into(abort_on),
                    body: body.into(),
                });
            }

            parsetree::SFor { var, start, step, end, body } => {
                push(_Stmt::SFor {
                    var: var,
                    start: spanned_into(start),
                    step: step.map(|e| spanned_into(e)),
                    end: spanned_into(end),
                    body: body.into(),
                });
            }

            parsetree::SForIn { vars, iter, body } => {
                push(_Stmt::SForIn {
                    vars: vars,
                    iter: vec_into(iter),
                    body: body.into(),
                });
            }
        }
    }
}

impl<'a> Conv<'a> for parsetree::CallArgs<'a> {
    type Target = Expr<'a>;

    fn conv<F>(self, mut push: F) where F: FnMut(Self::Target) {
        match self {
            parsetree::CallArgs::Normal(exprs) => {
                for expr in exprs {
                    push(spanned_into(expr))
                }
            }
            parsetree::CallArgs::String(s) => {
                // TODO use real span
                push(Spanned::default(ELit(Const::Str(s))))
            }
            parsetree::CallArgs::Table(cons) => {
                push(Spanned::default(ETable(conv_table(cons))))
            }
        }
    }
}
<commit_msg>remove unneeded lifetimes from AST conversion methods<commit_after>//! Parse tree -> AST conversion

// TODO Do resolve and fold, possibly some lints in this pass

use super::*;
use parser::parsetree;
use parser::span::Spanned;

use lea_core::constant::Const;

impl<'a> From<parsetree::Function<'a>> for Function<'a> {
    fn from(func: parsetree::Function<'a>) -> Self {
        Function {
            params: func.params,
            varargs: func.varargs,
            body: func.body.into(),
            locals: Default::default(),
            upvalues: Default::default(),
        }
    }
}

impl<'a> From<parsetree::Block<'a>> for Block<'a> {
    fn from(block: parsetree::Block<'a>) -> Self {
        Block {
            span: block.span,
            stmts: conv_vec(block.stmts),
            localmap: Default::default(),
        }
    }
}

impl<'a> From<parsetree::_Variable<'a>> for _Variable<'a> {
    fn from(var: parsetree::_Variable<'a>) -> Self {
        match var {
            parsetree::VNamed(name) => {
                VNamed(name)
            }
            parsetree::VIndex(var, index) => {
                VIndex(Box::new(spanned_into(*var)), match index {
                    parsetree::VarIndex::DotIndex(name) => {
                        Box::new(Spanned::new(name.span,
                            ELit(Const::Str(name.value.to_owned()))))
                    }
                    parsetree::VarIndex::ExprIndex(expr) => {
                        Box::new(spanned_into(*expr))
                    }
                })
            }
        }
    }
}

impl<'a> From<parsetree::_Expr<'a>> for _Expr<'a> {
    fn from(expr: parsetree::_Expr<'a>) -> Self {
        match expr {
            parsetree::ELit(lit) => ELit(lit),
            parsetree::EBinOp(lhs, op, rhs) =>
                EBinOp(Box::new(spanned_into(*lhs)), op, Box::new(spanned_into(*rhs))),
            parsetree::EUnOp(op, expr) => EUnOp(op, Box::new(spanned_into(*expr))),
            parsetree::EBraced(expr) => expr.value.into(),

            parsetree::EVar(var) => EVar(spanned_into(var)),

            parsetree::ECall(call) => ECall(match call {
                parsetree::SimpleCall(callee, args) => {
                    SimpleCall(Box::new(spanned_into(*callee)), conv_args(args))
                }
                parsetree::MethodCall(obj, name, args) => {
                    MethodCall(Box::new(spanned_into(*obj)), name, conv_args(args))
                }
            }),

            parsetree::ETable(cons) => {
                ETable(conv_table(cons))
            }

            parsetree::EFunc(func) => EFunc(func.into()),
            parsetree::EArray(elems) => EArray(vec_into(elems)),
            parsetree::EVarArgs => EVarArgs,
        }
    }
}

/// Implemented for parse tree nodes. Converts them into `n` instances of `Target`.
trait Conv<'a> {
    type Target;

    fn conv<F>(self, push: F) where F: FnMut(Self::Target);
}

fn conv_table<'a>(cons: parsetree::TableCons<'a>) -> Vec<(Expr<'a>, Expr<'a>)> {
    let mut entries: Vec<(Expr<'a>, Expr<'a>)> = Vec::new();
    let mut i = 0;  // XXX 1?
    for entry in cons {
        entries.push(match entry {
            parsetree::TableEntry::Pair(key, value) => {
                (spanned_into(key), spanned_into(value))
            }
            parsetree::TableEntry::Elem(value) => {
                let idx = i;
                i += 1;
                (Spanned::new(value.span, ELit(Const::Int(idx))), spanned_into(value))
            }
        });
    }

    entries
}

fn conv_vec<'a, T: Conv<'a>>(v: Vec<Spanned<T>>) -> Vec<Spanned<T::Target>> {
    let mut res = Vec::new();
    for item in v.into_iter() {
        let span = item.span;
        item.value.conv(|i| res.push(Spanned::new(span, i)));
    }

    res
}

fn conv_args(args: parsetree::CallArgs) -> Vec<Expr> {
    let mut v = Vec::new();
    args.conv(|e| v.push(e));

    v
}

fn vec_into<U, T: Into<U>>(v: Vec<Spanned<T>>) -> Vec<Spanned<U>> {
    v.into_iter().map(|item| spanned_into(item)).collect()
}

// Needed because Spanned<T> cannot impl Into. Not even specialization can help there.
fn spanned_into<U, T: Into<U>>(item: Spanned<T>) -> Spanned<U> {
    Spanned::new(item.span, item.value.into())
}

impl<'a> Conv<'a> for parsetree::_Stmt<'a> {
    type Target = _Stmt<'a>;

    fn conv<F>(self, mut push: F) where F: FnMut(Self::Target) {
        match self {
            parsetree::SDecl(names, exprs) => {
                push(_Stmt::SDecl(names, vec_into(exprs)));
            }

            parsetree::SAssign(vars, exprs) => {
                push(_Stmt::SAssign(vec_into(vars), vec_into(exprs)));
            }

            parsetree::SDo(block) => {
                push(_Stmt::SDo(block.into()));
            }

            parsetree::SBreak => {
                push(_Stmt::SBreak);
            }

            parsetree::SSemi => {}

            parsetree::SReturn(exprs) => {
                push(_Stmt::SReturn(vec_into(exprs)));
            }

            parsetree::SCall(call) => {
                push(_Stmt::SCall(match call {
                    parsetree::SimpleCall(callee, args) => {
                        SimpleCall(Box::new(spanned_into(*callee)), conv_args(args))
                    }
                    parsetree::MethodCall(obj, name, args) => {
                        MethodCall(Box::new(spanned_into(*obj)), name, conv_args(args))
                    }
                }));
            }

            parsetree::SFunc(var, func) => {
                push(_Stmt::SAssign(
                    vec![spanned_into(var)],
                    vec![Spanned::new(func.body.span, EFunc(func.into()))]
                ));
            }

            parsetree::SMethod(var, name, func) => {
                // var:name(args...)   =>   var.name = function(self, args...)
                let mut func: Function<'a> = func.into();
                func.params.insert(0, Spanned::new(name.span, "self"));

                push(_Stmt::SAssign(
                    vec![Spanned::new(var.span, VIndex(
                        Box::new(spanned_into(var)),
                        Box::new(Spanned::new(name.span, ELit(Const::Str(name.to_string()))))
                    ))],
                    vec![Spanned::new(func.body.span, EFunc(func))]
                ));
            }

            parsetree::SLFunc(local, func) => {
                push(_Stmt::SDecl(vec![local], vec![]));
                push(_Stmt::SAssign(
                    vec![Spanned::new(local.span, VNamed(local.value))],
                    vec![Spanned::new(func.body.span, EFunc(func.into()))]
                ));
            }

            parsetree::SIf { cond, body, elifs, el } => {
                // Build else block for this if statement
                // Start with "pure" else at the end
                let mut myelse: Option<Block> = el.map(|el| el.into());

                for elif in elifs {
                    let (cond, body) = elif.value;

                    myelse = Some(Block {
                        localmap: Default::default(),
                        span: elif.span,
                        stmts: vec![
                            Spanned::new(elif.span, SIf {
                                cond: spanned_into(cond),
                                body: body.into(),
                                el: myelse, // Old else block here
                            }),
                        ],
                    });
                }

                push(_Stmt::SIf {
                    cond: spanned_into(cond),
                    body: body.into(),
                    el: myelse,
                });
            }

            parsetree::SWhile { cond, body } => {
                push(_Stmt::SWhile {
                    cond: spanned_into(cond),
                    body: body.into(),
                });
            }

            parsetree::SRepeat { abort_on, body } => {
                push(_Stmt::SRepeat {
                    abort_on: spanned_into(abort_on),
                    body: body.into(),
                });
            }

            parsetree::SFor { var, start, step, end, body } => {
                push(_Stmt::SFor {
                    var: var,
                    start: spanned_into(start),
                    step: step.map(|e| spanned_into(e)),
                    end: spanned_into(end),
                    body: body.into(),
                });
            }

            parsetree::SForIn { vars, iter, body } => {
                push(_Stmt::SForIn {
                    vars: vars,
                    iter: vec_into(iter),
                    body: body.into(),
                });
            }
        }
    }
}

impl<'a> Conv<'a> for parsetree::CallArgs<'a> {
    type Target = Expr<'a>;

    fn conv<F>(self, mut push: F) where F: FnMut(Self::Target) {
        match self {
            parsetree::CallArgs::Normal(exprs) => {
                for expr in exprs {
                    push(spanned_into(expr))
                }
            }
            parsetree::CallArgs::String(s) => {
                // TODO use real span
                push(Spanned::default(ELit(Const::Str(s))))
            }
            parsetree::CallArgs::Table(cons) => {
                push(Spanned::default(ETable(conv_table(cons))))
            }
        }
    }
}
<|endoftext|>


<commit_before>#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub use self::x86::*;

mod x86 {
    pub unsafe fn atomic_set_bit<T>(nr: usize, addr: *mut T) {
        asm!("lock bts $1, $0"
             :: "=*m" (addr as *mut u32), "Ir" (nr)
             : "memory"
             : "volatile");
    }

    pub unsafe fn atomic_clear_bit<T>(nr: usize, addr: *mut T) {
        asm!("lock btr $1, $0"
             :: "=*m" (addr as *mut u32), "Ir" (nr)
             : "memory"
             : "volatile");
    }

    pub fn wmb() {
        unsafe {
            asm!("sfence" ::: "memory" : "volatile");
        }
    }

    #[test]
    pub fn test_set_and_clear() {
        let mut array = [0u32; 4];

        unsafe  {
            atomic_set_bit(1, &mut array[0] as *mut u32);
            atomic_set_bit(2, &mut array[0] as *mut u32);
            atomic_set_bit(3, &mut array[0] as *mut u32);
            atomic_set_bit(32, &mut array[0] as *mut u32);
            atomic_set_bit(33, &mut array[0] as *mut u32);
            atomic_set_bit(34, &mut array[0] as *mut u32);
            atomic_set_bit(127, &mut array[0] as *mut u32);

            assert_eq!(array[0], 0xE);
            assert_eq!(array[1], 0x7);
            assert_eq!(array[2], 0x0);
            assert_eq!(array[3], 0x80000000);

            atomic_clear_bit(1, &mut array[0] as *mut u32);
            atomic_clear_bit(2, &mut array[0] as *mut u32);
            atomic_clear_bit(3, &mut array[0] as *mut u32);
            atomic_clear_bit(32, &mut array[0] as *mut u32);
            atomic_clear_bit(33, &mut array[0] as *mut u32);
            atomic_clear_bit(34, &mut array[0] as *mut u32);
            atomic_clear_bit(127, &mut array[0] as *mut u32);

            assert_eq!(array[0], 0x0);
            assert_eq!(array[1], 0x0);
            assert_eq!(array[2], 0x0);
            assert_eq!(array[3], 0x0);
        }
    }
}
<commit_msg>libxen: Add first_bit()<commit_after>#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub use self::x86::*;

mod x86 {
    pub unsafe fn atomic_set_bit<T>(nr: usize, addr: *mut T) {
        asm!("lock bts $1, $0"
             :: "=*m" (addr as *mut u32), "Ir" (nr)
             : "memory"
             : "volatile");
    }

    pub unsafe fn atomic_clear_bit<T>(nr: usize, addr: *mut T) {
        asm!("lock btr $1, $0"
             :: "=*m" (addr as *mut u32), "Ir" (nr)
             : "memory"
             : "volatile");
    }

    pub fn first_bit(data: usize) -> usize {
        unsafe {
            let ret;

            asm!("bsf $0, $1\n\t\
                 jnz 1f\n\t
                 mov $$0, $0\n\t
                 1:\n\t"
                 : "=r" (ret)
                 : "r" (data)
                 :: "volatile");

            ret
        }
    }

    pub fn wmb() {
        unsafe {
            asm!("sfence" ::: "memory" : "volatile");
        }
    }

    #[test]
    pub fn test_set_and_clear() {
        let mut array = [0u32; 4];

        unsafe  {
            atomic_set_bit(1, &mut array[0] as *mut u32);
            atomic_set_bit(2, &mut array[0] as *mut u32);
            atomic_set_bit(3, &mut array[0] as *mut u32);
            atomic_set_bit(32, &mut array[0] as *mut u32);
            atomic_set_bit(33, &mut array[0] as *mut u32);
            atomic_set_bit(34, &mut array[0] as *mut u32);
            atomic_set_bit(127, &mut array[0] as *mut u32);

            assert_eq!(array[0], 0xE);
            assert_eq!(array[1], 0x7);
            assert_eq!(array[2], 0x0);
            assert_eq!(array[3], 0x80000000);

            atomic_clear_bit(1, &mut array[0] as *mut u32);
            atomic_clear_bit(2, &mut array[0] as *mut u32);
            atomic_clear_bit(3, &mut array[0] as *mut u32);
            atomic_clear_bit(32, &mut array[0] as *mut u32);
            atomic_clear_bit(33, &mut array[0] as *mut u32);
            atomic_clear_bit(34, &mut array[0] as *mut u32);
            atomic_clear_bit(127, &mut array[0] as *mut u32);

            assert_eq!(array[0], 0x0);
            assert_eq!(array[1], 0x0);
            assert_eq!(array[2], 0x0);
            assert_eq!(array[3], 0x0);
        }
    }

    #[test]
    pub fn test_first_bit() {
        assert_eq!(first_bit(0x0), 0);
        assert_eq!(first_bit(0x1), 0);
        assert_eq!(first_bit(0x2), 1);
        assert_eq!(first_bit(0x3), 0);
        assert_eq!(first_bit(0x8000), 15);
        assert_eq!(first_bit(0xFF80), 7);
        assert_eq!(first_bit(0x80000000), 31);
    }
}
<|endoftext|>