<commit_before>//! `SourceBinder` should be the main entry point for getting info about source code.
//! It's main task is to map source syntax trees to hir-level IDs.
//!
//! It is intended to subsume `FromSource` and `SourceAnalyzer`.

use hir_def::{
    child_by_source::ChildBySource,
    dyn_map::DynMap,
    keys::{self, Key},
    resolver::{HasResolver, Resolver},
    ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId,
    StaticId, StructFieldId, StructId, TraitId, TypeAliasId, UnionId, VariantId,
};
use hir_expand::{AstId, InFile, MacroDefId, MacroDefKind};
use ra_prof::profile;
use ra_syntax::{ast, match_ast, AstNode, SyntaxNode, TextUnit};
use rustc_hash::FxHashMap;

use crate::{db::HirDatabase, Local, ModuleSource, SourceAnalyzer, TypeParam};

pub struct SourceBinder<'a, DB> {
    pub db: &'a DB,
    child_by_source_cache: FxHashMap<ChildContainer, DynMap>,
}

impl<DB: HirDatabase> SourceBinder<'_, DB> {
    pub fn new(db: &DB) -> SourceBinder<DB> {
        SourceBinder { db, child_by_source_cache: FxHashMap::default() }
    }

    pub fn analyze(
        &mut self,
        src: InFile<&SyntaxNode>,
        offset: Option<TextUnit>,
    ) -> SourceAnalyzer {
        let _p = profile("SourceBinder::analyzer");
        let container = match self.find_container(src) {
            Some(it) => it,
            None => return SourceAnalyzer::new_for_resolver(Resolver::default(), src),
        };

        let resolver = match container {
            ChildContainer::DefWithBodyId(def) => {
                return SourceAnalyzer::new_for_body(self.db, def, src, offset)
            }
            ChildContainer::TraitId(it) => it.resolver(self.db),
            ChildContainer::ImplId(it) => it.resolver(self.db),
            ChildContainer::ModuleId(it) => it.resolver(self.db),
            ChildContainer::EnumId(it) => it.resolver(self.db),
            ChildContainer::VariantId(it) => it.resolver(self.db),
        };
        SourceAnalyzer::new_for_resolver(resolver, src)
    }

    pub fn to_def<T: ToDef>(&mut self, src: InFile<T>) -> Option<T::Def> {
        T::to_def(self, src)
    }

    fn to_id<T: ToId>(&mut self, src: InFile<T>) -> Option<T::ID> {
        T::to_id(self, src)
    }

    fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> {
        for container in src.cloned().ancestors_with_macros(self.db).skip(1) {
            let res: ChildContainer = match_ast! {
                match (container.value) {
                    ast::TraitDef(it) => {
                        let def: TraitId = self.to_id(container.with_value(it))?;
                        def.into()
                    },
                    ast::ImplBlock(it) => {
                        let def: ImplId = self.to_id(container.with_value(it))?;
                        def.into()
                    },
                    ast::FnDef(it) => {
                        let def: FunctionId = self.to_id(container.with_value(it))?;
                        DefWithBodyId::from(def).into()
                    },
                    ast::StaticDef(it) => {
                        let def: StaticId = self.to_id(container.with_value(it))?;
                        DefWithBodyId::from(def).into()
                    },
                    ast::ConstDef(it) => {
                        let def: ConstId = self.to_id(container.with_value(it))?;
                        DefWithBodyId::from(def).into()
                    },
                    ast::EnumDef(it) => {
                        let def: EnumId = self.to_id(container.with_value(it))?;
                        def.into()
                    },
                    ast::StructDef(it) => {
                        let def: StructId = self.to_id(container.with_value(it))?;
                        VariantId::from(def).into()
                    },
                    ast::UnionDef(it) => {
                        let def: UnionId = self.to_id(container.with_value(it))?;
                        VariantId::from(def).into()
                    },
                    // FIXME: handle out-of-line modules here
                    _ => { continue },
                }
            };
            return Some(res);
        }

        let module_source = ModuleSource::from_child_node(self.db, src);
        let c = crate::Module::from_definition(self.db, src.with_value(module_source))?;
        Some(c.id.into())
    }
}

pub trait ToId: Sized {
    type ID: Sized + Copy + 'static;
    fn to_id<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>)
        -> Option<Self::ID>;
}

pub trait ToDef: Sized + AstNode + 'static {
    type Def;
    fn to_def<DB: HirDatabase>(
        sb: &mut SourceBinder<'_, DB>,
        src: InFile<Self>,
    ) -> Option<Self::Def>;
}

macro_rules! to_def_impls {
    ($(($def:path, $ast:path)),* ,) => {$(
        impl ToDef for $ast {
            type Def = $def;
            fn to_def<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>)
                -> Option<Self::Def>
            { sb.to_id(src).map(Into::into) }
        }
    )*}
}

to_def_impls![
    (crate::Struct, ast::StructDef),
    (crate::Enum, ast::EnumDef),
    (crate::Union, ast::UnionDef),
    (crate::Trait, ast::TraitDef),
    (crate::ImplBlock, ast::ImplBlock),
    (crate::TypeAlias, ast::TypeAliasDef),
    (crate::Const, ast::ConstDef),
    (crate::Static, ast::StaticDef),
    (crate::Function, ast::FnDef),
    (crate::StructField, ast::RecordFieldDef),
    (crate::EnumVariant, ast::EnumVariant),
    (crate::MacroDef, ast::MacroCall), // this one is dubious, not all calls are macros
];

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
enum ChildContainer {
    DefWithBodyId(DefWithBodyId),
    ModuleId(ModuleId),
    TraitId(TraitId),
    ImplId(ImplId),
    EnumId(EnumId),
    VariantId(VariantId),
}
impl_froms! {
    ChildContainer:
    DefWithBodyId,
    ModuleId,
    TraitId,
    ImplId,
    EnumId,
    VariantId,
}

pub trait ToIdByKey: Sized + AstNode + 'static {
    type ID: Sized + Copy + 'static;
    const KEY: Key<Self, Self::ID>;
}

impl<T: ToIdByKey> ToId for T {
    type ID = <T as ToIdByKey>::ID;
    fn to_id<DB: HirDatabase>(
        sb: &mut SourceBinder<'_, DB>,
        src: InFile<Self>,
    ) -> Option<Self::ID> {
        let container = sb.find_container(src.as_ref().map(|it| it.syntax()))?;
        let db = sb.db;
        let dyn_map =
            &*sb.child_by_source_cache.entry(container).or_insert_with(|| match container {
                ChildContainer::DefWithBodyId(it) => it.child_by_source(db),
                ChildContainer::ModuleId(it) => it.child_by_source(db),
                ChildContainer::TraitId(it) => it.child_by_source(db),
                ChildContainer::ImplId(it) => it.child_by_source(db),
                ChildContainer::EnumId(it) => it.child_by_source(db),
                ChildContainer::VariantId(it) => it.child_by_source(db),
            });
        dyn_map[T::KEY].get(&src).copied()
    }
}

macro_rules! to_id_key_impls {
    ($(($id:ident, $ast:path, $key:path)),* ,) => {$(
        impl ToIdByKey for $ast {
            type ID = $id;
            const KEY: Key<Self, Self::ID> = $key;
        }
    )*}
}

to_id_key_impls![
    (StructId, ast::StructDef, keys::STRUCT),
    (UnionId, ast::UnionDef, keys::UNION),
    (EnumId, ast::EnumDef, keys::ENUM),
    (TraitId, ast::TraitDef, keys::TRAIT),
    (FunctionId, ast::FnDef, keys::FUNCTION),
    (StaticId, ast::StaticDef, keys::STATIC),
    (ConstId, ast::ConstDef, keys::CONST),
    (TypeAliasId, ast::TypeAliasDef, keys::TYPE_ALIAS),
    (ImplId, ast::ImplBlock, keys::IMPL),
    (StructFieldId, ast::RecordFieldDef, keys::RECORD_FIELD),
    (EnumVariantId, ast::EnumVariant, keys::ENUM_VARIANT),
];

// FIXME: use DynMap as well?
impl ToId for ast::MacroCall {
    type ID = MacroDefId;
    fn to_id<DB: HirDatabase>(
        sb: &mut SourceBinder<'_, DB>,
        src: InFile<Self>,
    ) -> Option<Self::ID> {
        let kind = MacroDefKind::Declarative;

        let module_src = ModuleSource::from_child_node(sb.db, src.as_ref().map(|it| it.syntax()));
        let module = crate::Module::from_definition(sb.db, InFile::new(src.file_id, module_src))?;
        let krate = Some(module.krate().id);

        let ast_id =
            Some(AstId::new(src.file_id, sb.db.ast_id_map(src.file_id).ast_id(&src.value)));

        Some(MacroDefId { krate, ast_id, kind })
    }
}

impl ToDef for ast::BindPat {
    type Def = Local;

    fn to_def<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) -> Option<Local> {
        let file_id = src.file_id;
        let parent: DefWithBodyId = src.value.syntax().ancestors().find_map(|it| {
            let res = match_ast! {
                match it {
                    ast::ConstDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    ast::StaticDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    _ => return None,
                }
            };
            Some(res)
        })?;
        let (_body, source_map) = sb.db.body_with_source_map(parent);
        let src = src.map(ast::Pat::from);
        let pat_id = source_map.node_pat(src.as_ref())?;
        Some(Local { parent: parent.into(), pat_id })
    }
}

impl ToDef for ast::TypeParam {
    type Def = TypeParam;

    fn to_def<DB: HirDatabase>(
        sb: &mut SourceBinder<'_, DB>,
        src: InFile<ast::TypeParam>,
    ) -> Option<TypeParam> {
        let mut sb = SourceBinder::new(sb.db);
        let file_id = src.file_id;
        let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| {
            let res = match_ast! {
                match it {
                    ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    ast::StructDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    ast::EnumDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    ast::TraitDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    ast::TypeAliasDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    ast::ImplBlock(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    _ => return None,
                }
            };
            Some(res)
        })?;
        let &id = parent.child_by_source(sb.db)[keys::TYPE_PARAM].get(&src)?;
        Some(TypeParam { id })
    }
}
<commit_msg>Cache source for generics<commit_after>//! `SourceBinder` should be the main entry point for getting info about source code.
//! It's main task is to map source syntax trees to hir-level IDs.
//!
//! It is intended to subsume `FromSource` and `SourceAnalyzer`.

use hir_def::{
    child_by_source::ChildBySource,
    dyn_map::DynMap,
    keys::{self, Key},
    resolver::{HasResolver, Resolver},
    ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId,
    StaticId, StructFieldId, StructId, TraitId, TypeAliasId, UnionId, VariantId,
};
use hir_expand::{AstId, InFile, MacroDefId, MacroDefKind};
use ra_prof::profile;
use ra_syntax::{ast, match_ast, AstNode, SyntaxNode, TextUnit};
use rustc_hash::FxHashMap;

use crate::{db::HirDatabase, Local, ModuleSource, SourceAnalyzer, TypeParam};

pub struct SourceBinder<'a, DB> {
    pub db: &'a DB,
    child_by_source_cache: FxHashMap<ChildContainer, DynMap>,
}

impl<DB: HirDatabase> SourceBinder<'_, DB> {
    pub fn new(db: &DB) -> SourceBinder<DB> {
        SourceBinder { db, child_by_source_cache: FxHashMap::default() }
    }

    pub fn analyze(
        &mut self,
        src: InFile<&SyntaxNode>,
        offset: Option<TextUnit>,
    ) -> SourceAnalyzer {
        let _p = profile("SourceBinder::analyzer");
        let container = match self.find_container(src) {
            Some(it) => it,
            None => return SourceAnalyzer::new_for_resolver(Resolver::default(), src),
        };

        let resolver = match container {
            ChildContainer::DefWithBodyId(def) => {
                return SourceAnalyzer::new_for_body(self.db, def, src, offset)
            }
            ChildContainer::TraitId(it) => it.resolver(self.db),
            ChildContainer::ImplId(it) => it.resolver(self.db),
            ChildContainer::ModuleId(it) => it.resolver(self.db),
            ChildContainer::EnumId(it) => it.resolver(self.db),
            ChildContainer::VariantId(it) => it.resolver(self.db),
            ChildContainer::GenericDefId(it) => it.resolver(self.db),
        };
        SourceAnalyzer::new_for_resolver(resolver, src)
    }

    pub fn to_def<T: ToDef>(&mut self, src: InFile<T>) -> Option<T::Def> {
        T::to_def(self, src)
    }

    fn to_id<T: ToId>(&mut self, src: InFile<T>) -> Option<T::ID> {
        T::to_id(self, src)
    }

    fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> {
        for container in src.cloned().ancestors_with_macros(self.db).skip(1) {
            let res: ChildContainer = match_ast! {
                match (container.value) {
                    ast::TraitDef(it) => {
                        let def: TraitId = self.to_id(container.with_value(it))?;
                        def.into()
                    },
                    ast::ImplBlock(it) => {
                        let def: ImplId = self.to_id(container.with_value(it))?;
                        def.into()
                    },
                    ast::FnDef(it) => {
                        let def: FunctionId = self.to_id(container.with_value(it))?;
                        DefWithBodyId::from(def).into()
                    },
                    ast::StaticDef(it) => {
                        let def: StaticId = self.to_id(container.with_value(it))?;
                        DefWithBodyId::from(def).into()
                    },
                    ast::ConstDef(it) => {
                        let def: ConstId = self.to_id(container.with_value(it))?;
                        DefWithBodyId::from(def).into()
                    },
                    ast::EnumDef(it) => {
                        let def: EnumId = self.to_id(container.with_value(it))?;
                        def.into()
                    },
                    ast::StructDef(it) => {
                        let def: StructId = self.to_id(container.with_value(it))?;
                        VariantId::from(def).into()
                    },
                    ast::UnionDef(it) => {
                        let def: UnionId = self.to_id(container.with_value(it))?;
                        VariantId::from(def).into()
                    },
                    // FIXME: handle out-of-line modules here
                    _ => { continue },
                }
            };
            return Some(res);
        }

        let module_source = ModuleSource::from_child_node(self.db, src);
        let c = crate::Module::from_definition(self.db, src.with_value(module_source))?;
        Some(c.id.into())
    }

    fn child_by_source(&mut self, container: ChildContainer) -> &DynMap {
        let db = self.db;
        self.child_by_source_cache.entry(container).or_insert_with(|| match container {
            ChildContainer::DefWithBodyId(it) => it.child_by_source(db),
            ChildContainer::ModuleId(it) => it.child_by_source(db),
            ChildContainer::TraitId(it) => it.child_by_source(db),
            ChildContainer::ImplId(it) => it.child_by_source(db),
            ChildContainer::EnumId(it) => it.child_by_source(db),
            ChildContainer::VariantId(it) => it.child_by_source(db),
            ChildContainer::GenericDefId(it) => it.child_by_source(db),
        })
    }
}

pub trait ToId: Sized {
    type ID: Sized + Copy + 'static;
    fn to_id<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>)
        -> Option<Self::ID>;
}

pub trait ToDef: Sized + AstNode + 'static {
    type Def;
    fn to_def<DB: HirDatabase>(
        sb: &mut SourceBinder<'_, DB>,
        src: InFile<Self>,
    ) -> Option<Self::Def>;
}

macro_rules! to_def_impls {
    ($(($def:path, $ast:path)),* ,) => {$(
        impl ToDef for $ast {
            type Def = $def;
            fn to_def<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>)
                -> Option<Self::Def>
            { sb.to_id(src).map(Into::into) }
        }
    )*}
}

to_def_impls![
    (crate::Struct, ast::StructDef),
    (crate::Enum, ast::EnumDef),
    (crate::Union, ast::UnionDef),
    (crate::Trait, ast::TraitDef),
    (crate::ImplBlock, ast::ImplBlock),
    (crate::TypeAlias, ast::TypeAliasDef),
    (crate::Const, ast::ConstDef),
    (crate::Static, ast::StaticDef),
    (crate::Function, ast::FnDef),
    (crate::StructField, ast::RecordFieldDef),
    (crate::EnumVariant, ast::EnumVariant),
    (crate::MacroDef, ast::MacroCall), // this one is dubious, not all calls are macros
];

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
enum ChildContainer {
    DefWithBodyId(DefWithBodyId),
    ModuleId(ModuleId),
    TraitId(TraitId),
    ImplId(ImplId),
    EnumId(EnumId),
    VariantId(VariantId),
    /// XXX: this might be the same def as, for example an `EnumId`. However,
    /// here the children generic parameters, and not, eg enum variants.
    GenericDefId(GenericDefId),
}
impl_froms! {
    ChildContainer:
    DefWithBodyId,
    ModuleId,
    TraitId,
    ImplId,
    EnumId,
    VariantId,
    GenericDefId
}

pub trait ToIdByKey: Sized + AstNode + 'static {
    type ID: Sized + Copy + 'static;
    const KEY: Key<Self, Self::ID>;
}

impl<T: ToIdByKey> ToId for T {
    type ID = <T as ToIdByKey>::ID;
    fn to_id<DB: HirDatabase>(
        sb: &mut SourceBinder<'_, DB>,
        src: InFile<Self>,
    ) -> Option<Self::ID> {
        let container = sb.find_container(src.as_ref().map(|it| it.syntax()))?;
        let db = sb.db;
        let dyn_map =
            &*sb.child_by_source_cache.entry(container).or_insert_with(|| match container {
                ChildContainer::DefWithBodyId(it) => it.child_by_source(db),
                ChildContainer::ModuleId(it) => it.child_by_source(db),
                ChildContainer::TraitId(it) => it.child_by_source(db),
                ChildContainer::ImplId(it) => it.child_by_source(db),
                ChildContainer::EnumId(it) => it.child_by_source(db),
                ChildContainer::VariantId(it) => it.child_by_source(db),
                ChildContainer::GenericDefId(it) => it.child_by_source(db),
            });
        dyn_map[T::KEY].get(&src).copied()
    }
}

macro_rules! to_id_key_impls {
    ($(($id:ident, $ast:path, $key:path)),* ,) => {$(
        impl ToIdByKey for $ast {
            type ID = $id;
            const KEY: Key<Self, Self::ID> = $key;
        }
    )*}
}

to_id_key_impls![
    (StructId, ast::StructDef, keys::STRUCT),
    (UnionId, ast::UnionDef, keys::UNION),
    (EnumId, ast::EnumDef, keys::ENUM),
    (TraitId, ast::TraitDef, keys::TRAIT),
    (FunctionId, ast::FnDef, keys::FUNCTION),
    (StaticId, ast::StaticDef, keys::STATIC),
    (ConstId, ast::ConstDef, keys::CONST),
    (TypeAliasId, ast::TypeAliasDef, keys::TYPE_ALIAS),
    (ImplId, ast::ImplBlock, keys::IMPL),
    (StructFieldId, ast::RecordFieldDef, keys::RECORD_FIELD),
    (EnumVariantId, ast::EnumVariant, keys::ENUM_VARIANT),
];

// FIXME: use DynMap as well?
impl ToId for ast::MacroCall {
    type ID = MacroDefId;
    fn to_id<DB: HirDatabase>(
        sb: &mut SourceBinder<'_, DB>,
        src: InFile<Self>,
    ) -> Option<Self::ID> {
        let kind = MacroDefKind::Declarative;

        let module_src = ModuleSource::from_child_node(sb.db, src.as_ref().map(|it| it.syntax()));
        let module = crate::Module::from_definition(sb.db, InFile::new(src.file_id, module_src))?;
        let krate = Some(module.krate().id);

        let ast_id =
            Some(AstId::new(src.file_id, sb.db.ast_id_map(src.file_id).ast_id(&src.value)));

        Some(MacroDefId { krate, ast_id, kind })
    }
}

impl ToDef for ast::BindPat {
    type Def = Local;

    fn to_def<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) -> Option<Local> {
        let file_id = src.file_id;
        let parent: DefWithBodyId = src.value.syntax().ancestors().find_map(|it| {
            let res = match_ast! {
                match it {
                    ast::ConstDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    ast::StaticDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    _ => return None,
                }
            };
            Some(res)
        })?;
        let (_body, source_map) = sb.db.body_with_source_map(parent);
        let src = src.map(ast::Pat::from);
        let pat_id = source_map.node_pat(src.as_ref())?;
        Some(Local { parent: parent.into(), pat_id })
    }
}

impl ToDef for ast::TypeParam {
    type Def = TypeParam;

    fn to_def<DB: HirDatabase>(
        sb: &mut SourceBinder<'_, DB>,
        src: InFile<ast::TypeParam>,
    ) -> Option<TypeParam> {
        let mut sb = SourceBinder::new(sb.db);
        let file_id = src.file_id;
        let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| {
            let res = match_ast! {
                match it {
                    ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    ast::StructDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    ast::EnumDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    ast::TraitDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    ast::TypeAliasDef(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    ast::ImplBlock(value) => { sb.to_id(InFile { value, file_id})?.into() },
                    _ => return None,
                }
            };
            Some(res)
        })?;
        let &id = sb.child_by_source(parent.into())[keys::TYPE_PARAM].get(&src)?;
        Some(TypeParam { id })
    }
}
<|endoftext|>


<commit_before>//! Project loading & configuration updates
use std::{mem, sync::Arc};

use base_db::{CrateGraph, SourceRoot, VfsPath};
use flycheck::{FlycheckConfig, FlycheckHandle};
use ide::AnalysisChange;
use project_model::{ProcMacroClient, ProjectWorkspace};
use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};

use crate::{
    config::{Config, FilesWatcher, LinkedProject},
    global_state::{GlobalState, Status},
    lsp_ext,
    main_loop::Task,
};
use lsp_ext::StatusParams;

impl GlobalState {
    pub(crate) fn update_configuration(&mut self, config: Config) {
        let _p = profile::span("GlobalState::update_configuration");
        let old_config = mem::replace(&mut self.config, config);
        if self.config.lru_capacity != old_config.lru_capacity {
            self.analysis_host.update_lru_capacity(old_config.lru_capacity);
        }
        if self.config.linked_projects != old_config.linked_projects {
            self.fetch_workspaces()
        } else if self.config.flycheck != old_config.flycheck {
            self.reload_flycheck();
        }
    }
    pub(crate) fn maybe_refresh(&mut self, changes: &[(AbsPathBuf, ChangeKind)]) {
        if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) {
            return;
        }
        match self.status {
            Status::Loading | Status::NeedsReload => return,
            Status::Ready | Status::Invalid => (),
        }
        if self.config.cargo_autoreload {
            self.fetch_workspaces();
        } else {
            self.transition(Status::NeedsReload);
        }

        fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool {
            const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
            const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];

            if path.ends_with("Cargo.toml") || path.ends_with("Cargo.lock") {
                return true;
            }
            if change_kind == ChangeKind::Modify {
                return false;
            }
            if path.extension().unwrap_or_default() != "rs" {
                return false;
            }
            if IMPLICIT_TARGET_FILES.iter().any(|it| path.ends_with(it)) {
                return true;
            }
            let parent = match path.parent() {
                Some(it) => it,
                None => return false,
            };
            if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.ends_with(it)) {
                return true;
            }
            if path.ends_with("main.rs") {
                let grand_parent = match parent.parent() {
                    Some(it) => it,
                    None => return false,
                };
                if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.ends_with(it)) {
                    return true;
                }
            }
            false
        }
    }
    pub(crate) fn transition(&mut self, new_status: Status) {
        self.status = new_status;
        if self.config.client_caps.status_notification {
            let lsp_status = match new_status {
                Status::Loading => lsp_ext::Status::Loading,
                Status::Ready => lsp_ext::Status::Ready,
                Status::Invalid => lsp_ext::Status::Invalid,
                Status::NeedsReload => lsp_ext::Status::NeedsReload,
            };
            self.send_notification::<lsp_ext::StatusNotification>(StatusParams {
                status: lsp_status,
            });
        }
    }
    pub(crate) fn fetch_workspaces(&mut self) {
        log::info!("will fetch workspaces");
        self.task_pool.handle.spawn({
            let linked_projects = self.config.linked_projects.clone();
            let cargo_config = self.config.cargo.clone();
            let with_sysroot = self.config.with_sysroot.clone();
            move || {
                let workspaces = linked_projects
                    .iter()
                    .map(|project| match project {
                        LinkedProject::ProjectManifest(manifest) => {
                            project_model::ProjectWorkspace::load(
                                manifest.clone(),
                                &cargo_config,
                                with_sysroot,
                            )
                        }
                        LinkedProject::InlineJsonProject(it) => {
                            project_model::ProjectWorkspace::load_inline(it.clone())
                        }
                    })
                    .collect::<Vec<_>>();
                log::info!("did fetch workspaces {:?}", workspaces);
                Task::Workspaces(workspaces)
            }
        });
    }
    pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) {
        let _p = profile::span("GlobalState::switch_workspaces");
        log::info!("will switch workspaces: {:?}", workspaces);

        let mut has_errors = false;
        let workspaces = workspaces
            .into_iter()
            .filter_map(|res| {
                res.map_err(|err| {
                    has_errors = true;
                    log::error!("failed to load workspace: {:#}", err);
                    if self.workspaces.is_empty() {
                        self.show_message(
                            lsp_types::MessageType::Error,
                            format!("rust-analyzer failed to load workspace: {:#}", err),
                        );
                    }
                })
                .ok()
            })
            .collect::<Vec<_>>();

        if &*self.workspaces == &workspaces {
            return;
        }

        if !self.workspaces.is_empty() && has_errors {
            return;
        }

        if let FilesWatcher::Client = self.config.files.watcher {
            let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
                watchers: workspaces
                    .iter()
                    .flat_map(ProjectWorkspace::to_roots)
                    .filter(|it| it.is_member)
                    .flat_map(|root| {
                        root.include.into_iter().map(|it| format!("{}/**/*.rs", it.display()))
                    })
                    .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None })
                    .collect(),
            };
            let registration = lsp_types::Registration {
                id: "workspace/didChangeWatchedFiles".to_string(),
                method: "workspace/didChangeWatchedFiles".to_string(),
                register_options: Some(serde_json::to_value(registration_options).unwrap()),
            };
            self.send_request::<lsp_types::request::RegisterCapability>(
                lsp_types::RegistrationParams { registrations: vec![registration] },
                |_, _| (),
            );
        }

        let mut change = AnalysisChange::new();

        let project_folders = ProjectFolders::new(&workspaces);

        self.proc_macro_client = match &self.config.proc_macro_srv {
            None => ProcMacroClient::dummy(),
            Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) {
                Ok(it) => it,
                Err(err) => {
                    log::error!(
                        "Failed to run proc_macro_srv from path {}, error: {:?}",
                        path.display(),
                        err
                    );
                    ProcMacroClient::dummy()
                }
            },
        };

        let watch = match self.config.files.watcher {
            FilesWatcher::Client => vec![],
            FilesWatcher::Notify => project_folders.watch,
        };
        self.loader.handle.set_config(vfs::loader::Config { load: project_folders.load, watch });

        // Create crate graph from all the workspaces
        let crate_graph = {
            let mut crate_graph = CrateGraph::default();
            let vfs = &mut self.vfs.write().0;
            let loader = &mut self.loader;
            let mut load = |path: &AbsPath| {
                let contents = loader.handle.load_sync(path);
                let path = vfs::VfsPath::from(path.to_path_buf());
                vfs.set_file_contents(path.clone(), contents);
                vfs.file_id(&path)
            };
            for ws in workspaces.iter() {
                crate_graph.extend(ws.to_crate_graph(
                    self.config.cargo.target.as_deref(),
                    &self.proc_macro_client,
                    &mut load,
                ));
            }

            crate_graph
        };
        change.set_crate_graph(crate_graph);

        self.source_root_config = project_folders.source_root_config;
        self.workspaces = Arc::new(workspaces);

        self.analysis_host.apply_change(change);
        self.process_changes();
        self.reload_flycheck();
        log::info!("did switch workspaces");
    }

    fn reload_flycheck(&mut self) {
        let config = match self.config.flycheck.clone() {
            Some(it) => it,
            None => {
                self.flycheck = None;
                return;
            }
        };

        let sender = self.flycheck_sender.clone();
        let sender = Box::new(move |msg| sender.send(msg).unwrap());
        self.flycheck = self
            .workspaces
            .iter()
            // FIXME: Figure out the multi-workspace situation
            .find_map(|w| match w {
                ProjectWorkspace::Cargo { cargo, sysroot: _ } => Some(cargo.workspace_root()),
                ProjectWorkspace::Json { project, .. } => {
                    // Enable flychecks for json projects if a custom flycheck command was supplied
                    // in the workspace configuration.
                    match config {
                        FlycheckConfig::CustomCommand { .. } => project.path(),
                        _ => None,
                    }
                }
            })
            .map(move |root| FlycheckHandle::spawn(sender, config, root.to_path_buf().into()))
    }
}

#[derive(Default)]
pub(crate) struct ProjectFolders {
    pub(crate) load: Vec<vfs::loader::Entry>,
    pub(crate) watch: Vec<usize>,
    pub(crate) source_root_config: SourceRootConfig,
}

impl ProjectFolders {
    pub(crate) fn new(workspaces: &[ProjectWorkspace]) -> ProjectFolders {
        let mut res = ProjectFolders::default();
        let mut fsc = FileSetConfig::builder();
        let mut local_filesets = vec![];

        for root in workspaces.iter().flat_map(|it| it.to_roots()) {
            let file_set_roots: Vec<VfsPath> =
                root.include.iter().cloned().map(VfsPath::from).collect();

            let entry = {
                let mut dirs = vfs::loader::Directories::default();
                dirs.extensions.push("rs".into());
                dirs.include.extend(root.include);
                dirs.exclude.extend(root.exclude);
                vfs::loader::Entry::Directories(dirs)
            };

            if root.is_member {
                res.watch.push(res.load.len());
            }
            res.load.push(entry);

            if root.is_member {
                local_filesets.push(fsc.len());
            }
            fsc.add_file_set(file_set_roots)
        }

        let fsc = fsc.build();
        res.source_root_config = SourceRootConfig { fsc, local_filesets };

        res
    }
}

#[derive(Default, Debug)]
pub(crate) struct SourceRootConfig {
    pub(crate) fsc: FileSetConfig,
    pub(crate) local_filesets: Vec<usize>,
}

impl SourceRootConfig {
    pub(crate) fn partition(&self, vfs: &vfs::Vfs) -> Vec<SourceRoot> {
        let _p = profile::span("SourceRootConfig::partition");
        self.fsc
            .partition(vfs)
            .into_iter()
            .enumerate()
            .map(|(idx, file_set)| {
                let is_local = self.local_filesets.contains(&idx);
                if is_local {
                    SourceRoot::new_local(file_set)
                } else {
                    SourceRoot::new_library(file_set)
                }
            })
            .collect()
    }
}
<commit_msg>Don't re-read open files from disk when reloading a workspace<commit_after>//! Project loading & configuration updates
use std::{mem, sync::Arc};

use base_db::{CrateGraph, SourceRoot, VfsPath};
use flycheck::{FlycheckConfig, FlycheckHandle};
use ide::AnalysisChange;
use project_model::{ProcMacroClient, ProjectWorkspace};
use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};

use crate::{
    config::{Config, FilesWatcher, LinkedProject},
    global_state::{GlobalState, Status},
    lsp_ext,
    main_loop::Task,
};
use lsp_ext::StatusParams;

impl GlobalState {
    pub(crate) fn update_configuration(&mut self, config: Config) {
        let _p = profile::span("GlobalState::update_configuration");
        let old_config = mem::replace(&mut self.config, config);
        if self.config.lru_capacity != old_config.lru_capacity {
            self.analysis_host.update_lru_capacity(old_config.lru_capacity);
        }
        if self.config.linked_projects != old_config.linked_projects {
            self.fetch_workspaces()
        } else if self.config.flycheck != old_config.flycheck {
            self.reload_flycheck();
        }
    }
    pub(crate) fn maybe_refresh(&mut self, changes: &[(AbsPathBuf, ChangeKind)]) {
        if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) {
            return;
        }
        match self.status {
            Status::Loading | Status::NeedsReload => return,
            Status::Ready | Status::Invalid => (),
        }
        if self.config.cargo_autoreload {
            self.fetch_workspaces();
        } else {
            self.transition(Status::NeedsReload);
        }

        fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool {
            const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
            const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];

            if path.ends_with("Cargo.toml") || path.ends_with("Cargo.lock") {
                return true;
            }
            if change_kind == ChangeKind::Modify {
                return false;
            }
            if path.extension().unwrap_or_default() != "rs" {
                return false;
            }
            if IMPLICIT_TARGET_FILES.iter().any(|it| path.ends_with(it)) {
                return true;
            }
            let parent = match path.parent() {
                Some(it) => it,
                None => return false,
            };
            if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.ends_with(it)) {
                return true;
            }
            if path.ends_with("main.rs") {
                let grand_parent = match parent.parent() {
                    Some(it) => it,
                    None => return false,
                };
                if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.ends_with(it)) {
                    return true;
                }
            }
            false
        }
    }
    pub(crate) fn transition(&mut self, new_status: Status) {
        self.status = new_status;
        if self.config.client_caps.status_notification {
            let lsp_status = match new_status {
                Status::Loading => lsp_ext::Status::Loading,
                Status::Ready => lsp_ext::Status::Ready,
                Status::Invalid => lsp_ext::Status::Invalid,
                Status::NeedsReload => lsp_ext::Status::NeedsReload,
            };
            self.send_notification::<lsp_ext::StatusNotification>(StatusParams {
                status: lsp_status,
            });
        }
    }
    pub(crate) fn fetch_workspaces(&mut self) {
        log::info!("will fetch workspaces");
        self.task_pool.handle.spawn({
            let linked_projects = self.config.linked_projects.clone();
            let cargo_config = self.config.cargo.clone();
            let with_sysroot = self.config.with_sysroot.clone();
            move || {
                let workspaces = linked_projects
                    .iter()
                    .map(|project| match project {
                        LinkedProject::ProjectManifest(manifest) => {
                            project_model::ProjectWorkspace::load(
                                manifest.clone(),
                                &cargo_config,
                                with_sysroot,
                            )
                        }
                        LinkedProject::InlineJsonProject(it) => {
                            project_model::ProjectWorkspace::load_inline(it.clone())
                        }
                    })
                    .collect::<Vec<_>>();
                log::info!("did fetch workspaces {:?}", workspaces);
                Task::Workspaces(workspaces)
            }
        });
    }
    pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) {
        let _p = profile::span("GlobalState::switch_workspaces");
        log::info!("will switch workspaces: {:?}", workspaces);

        let mut has_errors = false;
        let workspaces = workspaces
            .into_iter()
            .filter_map(|res| {
                res.map_err(|err| {
                    has_errors = true;
                    log::error!("failed to load workspace: {:#}", err);
                    if self.workspaces.is_empty() {
                        self.show_message(
                            lsp_types::MessageType::Error,
                            format!("rust-analyzer failed to load workspace: {:#}", err),
                        );
                    }
                })
                .ok()
            })
            .collect::<Vec<_>>();

        if &*self.workspaces == &workspaces {
            return;
        }

        if !self.workspaces.is_empty() && has_errors {
            return;
        }

        if let FilesWatcher::Client = self.config.files.watcher {
            let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
                watchers: workspaces
                    .iter()
                    .flat_map(ProjectWorkspace::to_roots)
                    .filter(|it| it.is_member)
                    .flat_map(|root| {
                        root.include.into_iter().map(|it| format!("{}/**/*.rs", it.display()))
                    })
                    .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None })
                    .collect(),
            };
            let registration = lsp_types::Registration {
                id: "workspace/didChangeWatchedFiles".to_string(),
                method: "workspace/didChangeWatchedFiles".to_string(),
                register_options: Some(serde_json::to_value(registration_options).unwrap()),
            };
            self.send_request::<lsp_types::request::RegisterCapability>(
                lsp_types::RegistrationParams { registrations: vec![registration] },
                |_, _| (),
            );
        }

        let mut change = AnalysisChange::new();

        let project_folders = ProjectFolders::new(&workspaces);

        self.proc_macro_client = match &self.config.proc_macro_srv {
            None => ProcMacroClient::dummy(),
            Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) {
                Ok(it) => it,
                Err(err) => {
                    log::error!(
                        "Failed to run proc_macro_srv from path {}, error: {:?}",
                        path.display(),
                        err
                    );
                    ProcMacroClient::dummy()
                }
            },
        };

        let watch = match self.config.files.watcher {
            FilesWatcher::Client => vec![],
            FilesWatcher::Notify => project_folders.watch,
        };
        self.loader.handle.set_config(vfs::loader::Config { load: project_folders.load, watch });

        // Create crate graph from all the workspaces
        let crate_graph = {
            let mut crate_graph = CrateGraph::default();
            let vfs = &mut self.vfs.write().0;
            let loader = &mut self.loader;
            let mem_docs = &self.mem_docs;
            let mut load = |path: &AbsPath| {
                let vfs_path = vfs::VfsPath::from(path.to_path_buf());
                if !mem_docs.contains_key(&vfs_path) {
                    let contents = loader.handle.load_sync(path);
                    vfs.set_file_contents(vfs_path.clone(), contents);
                }
                vfs.file_id(&vfs_path)
            };
            for ws in workspaces.iter() {
                crate_graph.extend(ws.to_crate_graph(
                    self.config.cargo.target.as_deref(),
                    &self.proc_macro_client,
                    &mut load,
                ));
            }

            crate_graph
        };
        change.set_crate_graph(crate_graph);

        self.source_root_config = project_folders.source_root_config;
        self.workspaces = Arc::new(workspaces);

        self.analysis_host.apply_change(change);
        self.process_changes();
        self.reload_flycheck();
        log::info!("did switch workspaces");
    }

    fn reload_flycheck(&mut self) {
        let config = match self.config.flycheck.clone() {
            Some(it) => it,
            None => {
                self.flycheck = None;
                return;
            }
        };

        let sender = self.flycheck_sender.clone();
        let sender = Box::new(move |msg| sender.send(msg).unwrap());
        self.flycheck = self
            .workspaces
            .iter()
            // FIXME: Figure out the multi-workspace situation
            .find_map(|w| match w {
                ProjectWorkspace::Cargo { cargo, sysroot: _ } => Some(cargo.workspace_root()),
                ProjectWorkspace::Json { project, .. } => {
                    // Enable flychecks for json projects if a custom flycheck command was supplied
                    // in the workspace configuration.
                    match config {
                        FlycheckConfig::CustomCommand { .. } => project.path(),
                        _ => None,
                    }
                }
            })
            .map(move |root| FlycheckHandle::spawn(sender, config, root.to_path_buf().into()))
    }
}

#[derive(Default)]
pub(crate) struct ProjectFolders {
    pub(crate) load: Vec<vfs::loader::Entry>,
    pub(crate) watch: Vec<usize>,
    pub(crate) source_root_config: SourceRootConfig,
}

impl ProjectFolders {
    pub(crate) fn new(workspaces: &[ProjectWorkspace]) -> ProjectFolders {
        let mut res = ProjectFolders::default();
        let mut fsc = FileSetConfig::builder();
        let mut local_filesets = vec![];

        for root in workspaces.iter().flat_map(|it| it.to_roots()) {
            let file_set_roots: Vec<VfsPath> =
                root.include.iter().cloned().map(VfsPath::from).collect();

            let entry = {
                let mut dirs = vfs::loader::Directories::default();
                dirs.extensions.push("rs".into());
                dirs.include.extend(root.include);
                dirs.exclude.extend(root.exclude);
                vfs::loader::Entry::Directories(dirs)
            };

            if root.is_member {
                res.watch.push(res.load.len());
            }
            res.load.push(entry);

            if root.is_member {
                local_filesets.push(fsc.len());
            }
            fsc.add_file_set(file_set_roots)
        }

        let fsc = fsc.build();
        res.source_root_config = SourceRootConfig { fsc, local_filesets };

        res
    }
}

#[derive(Default, Debug)]
pub(crate) struct SourceRootConfig {
    pub(crate) fsc: FileSetConfig,
    pub(crate) local_filesets: Vec<usize>,
}

impl SourceRootConfig {
    pub(crate) fn partition(&self, vfs: &vfs::Vfs) -> Vec<SourceRoot> {
        let _p = profile::span("SourceRootConfig::partition");
        self.fsc
            .partition(vfs)
            .into_iter()
            .enumerate()
            .map(|(idx, file_set)| {
                let is_local = self.local_filesets.contains(&idx);
                if is_local {
                    SourceRoot::new_local(file_set)
                } else {
                    SourceRoot::new_library(file_set)
                }
            })
            .collect()
    }
}
<|endoftext|>


<commit_before>extern crate futures;
extern crate futures_channel;
extern crate futures_executor;

use std::thread;

use futures::prelude::*;
use futures_channel::mpsc::*;
use futures_executor::current_thread::run;

#[test]
fn smoke() {
    let (mut sender, receiver) = channel(1);

    let t = thread::spawn(move || {
        run(|c| {
            while let Ok(s) = c.block_on(sender.send(42)) {
                sender = s;
            }
        });
    });

    let _s = run(|c| c.block_on(receiver.take(3).for_each(|_| Ok(())))).unwrap();

    t.join().unwrap()
}
<commit_msg>Fix mpsc-close.<commit_after>extern crate futures;
extern crate futures_channel;
extern crate futures_executor;

use std::thread;

use futures::prelude::*;
use futures_channel::mpsc::*;
use futures_executor::current_thread::run;

#[test]
fn smoke() {
    let (mut sender, receiver) = channel(1);

    let t = thread::spawn(move || {
        run(|c| {
            while let Ok(s) = c.block_on(sender.send(42)) {
                sender = s;
            }
        });
    });

    // `receiver` needs to be dropped for `sender` to stop sending and therefore before the join.
    let _ = run(|c| c.block_on(receiver.take(3).for_each(|_| Ok(())))).unwrap();

    t.join().unwrap()
}
<|endoftext|>


<commit_before>use parking_lot::{Condvar, Mutex};
use std::cell::RefCell;
use std::convert::From;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;

use crate::gc::{tlab, Address, Region, K};
use crate::handle::HandleMemory;
use crate::stack::DoraToNativeInfo;
use crate::vm::{get_vm, VM};

pub const STACK_SIZE: usize = 500 * K;

thread_local! {
    pub static THREAD: RefCell<Arc<DoraThread>> = RefCell::new(DoraThread::main());
}

pub struct Threads {
    pub threads: Mutex<Vec<Arc<DoraThread>>>,
    pub cond_join: Condvar,

    pub next_id: AtomicUsize,

    pub barrier: Barrier,
}

impl Threads {
    pub fn new() -> Threads {
        Threads {
            threads: Mutex::new(Vec::new()),
            cond_join: Condvar::new(),
            next_id: AtomicUsize::new(1),
            barrier: Barrier::new(),
        }
    }

    pub fn attach_current_thread(&self) {
        THREAD.with(|thread| {
            let mut threads = self.threads.lock();
            threads.push(thread.borrow().clone());
        });
    }

    pub fn attach_thread(&self, vm: &VM, thread: Arc<DoraThread>) {
        THREAD.with(|thread_local_thread| {
            thread_local_thread.borrow().park(vm);

            {
                let mut threads = self.threads.lock();
                threads.push(thread);
            }

            thread_local_thread.borrow().unpark(vm);
        });
    }

    pub fn next_id(&self) -> usize {
        self.next_id.fetch_add(1, Ordering::SeqCst)
    }

    pub fn detach_current_thread(&self) {
        let vm = get_vm();

        // Other threads might still be running and perform a GC.
        // Fill the TLAB for them.
        tlab::make_iterable_current(vm);

        THREAD.with(|thread| {
            thread.borrow().park(vm);
            let mut threads = self.threads.lock();
            threads.retain(|elem| !Arc::ptr_eq(elem, &*thread.borrow()));
            self.cond_join.notify_all();
        });
    }

    pub fn join_all(&self) {
        let mut threads = self.threads.lock();

        while threads.len() > 0 {
            self.cond_join.wait(&mut threads);
        }
    }

    pub fn each<F>(&self, mut f: F)
    where
        F: FnMut(&Arc<DoraThread>),
    {
        let threads = self.threads.lock();

        for thread in threads.iter() {
            f(thread)
        }
    }
}

pub struct DoraThread {
    pub id: AtomicUsize,
    pub handles: HandleMemory,
    pub tld: ThreadLocalData,
    pub saved_pc: AtomicUsize,
    pub saved_fp: AtomicUsize,
    pub state: AtomicUsize,
}

unsafe impl Sync for DoraThread {}
unsafe impl Send for DoraThread {}

impl DoraThread {
    pub fn new(vm: &VM) -> Arc<DoraThread> {
        DoraThread::with_id(vm.threads.next_id(), ThreadState::Parked)
    }

    pub fn main() -> Arc<DoraThread> {
        DoraThread::with_id(0, ThreadState::Running)
    }

    fn with_id(id: usize, initial_state: ThreadState) -> Arc<DoraThread> {
        Arc::new(DoraThread {
            id: AtomicUsize::new(id),
            handles: HandleMemory::new(),
            tld: ThreadLocalData::new(),
            saved_pc: AtomicUsize::new(0),
            saved_fp: AtomicUsize::new(0),
            state: AtomicUsize::new(initial_state as usize),
        })
    }

    pub fn id(&self) -> usize {
        self.id.load(Ordering::Relaxed)
    }

    pub fn dtn(&self) -> *const DoraToNativeInfo {
        self.tld.dtn.load(Ordering::Relaxed) as *const _
    }

    pub fn set_dtn(&self, ptr: *const DoraToNativeInfo) {
        self.tld.dtn.store(ptr as usize, Ordering::Relaxed);
    }

    pub fn use_dtn<F, R>(&self, dtn: &mut DoraToNativeInfo, fct: F) -> R
    where
        F: FnOnce() -> R,
    {
        dtn.last = self.dtn();

        self.set_dtn(dtn as *const _);

        let ret = fct();

        self.set_dtn(dtn.last);

        ret
    }

    pub fn push_dtn(&self, dtn: &mut DoraToNativeInfo) {
        dtn.last = self.dtn();
        self.set_dtn(dtn as *const _);
    }

    pub fn pop_dtn(&self) {
        let current_dtn = self.dtn();
        assert!(!current_dtn.is_null());
        let dtn = unsafe { &*current_dtn };
        self.set_dtn(dtn.last);
    }

    pub fn state_relaxed(&self) -> ThreadState {
        self.state.load(Ordering::Relaxed).into()
    }

    pub fn park(&self, vm: &VM) {
        if self
            .state
            .compare_exchange(
                ThreadState::Running as usize,
                ThreadState::Parked as usize,
                Ordering::SeqCst,
                Ordering::SeqCst,
            )
            .is_err()
        {
            self.park_slow(vm);
        }
    }

    pub fn park_slow(&self, vm: &VM) {
        assert!(self
            .state
            .compare_exchange(
                ThreadState::SafepointRequested as usize,
                ThreadState::ParkedSafepoint as usize,
                Ordering::SeqCst,
                Ordering::SeqCst,
            )
            .is_ok());
        vm.threads.barrier.notify_park();
    }

    pub fn unpark(&self, vm: &VM) {
        if self
            .state
            .compare_exchange(
                ThreadState::Parked as usize,
                ThreadState::Running as usize,
                Ordering::SeqCst,
                Ordering::SeqCst,
            )
            .is_err()
        {
            self.unpark_slow(vm);
        }
    }

    pub fn unpark_slow(&self, vm: &VM) {
        loop {
            match self.state.compare_exchange(
                ThreadState::Parked as usize,
                ThreadState::Running as usize,
                Ordering::SeqCst,
                Ordering::SeqCst,
            ) {
                Ok(_) => break,
                Err(state) => {
                    assert_eq!(state, ThreadState::ParkedSafepoint as usize);
                    vm.threads.barrier.wait_in_unpark();
                }
            }
        }
    }
}

pub struct StateManager {
    mtx: Mutex<(ThreadState, usize)>,
}

impl StateManager {
    fn new(initial_state: ThreadState) -> StateManager {
        StateManager {
            mtx: Mutex::new((initial_state, 0)),
        }
    }

    fn state(&self) -> ThreadState {
        let mtx = self.mtx.lock();
        mtx.0
    }

    fn park(&self, _vm: &VM) {
        let mut mtx = self.mtx.lock();
        assert!(mtx.0.is_running());
        mtx.0 = ThreadState::Parked;
    }

    fn unpark(&self, _vm: &VM) {
        let mut mtx = self.mtx.lock();
        assert!(mtx.0.is_parked());
        mtx.0 = ThreadState::Running;
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum ThreadState {
    Running = 0,
    Parked = 1,
    SafepointRequested = 2,
    ParkedSafepoint = 3,
    Safepoint = 4,
}

impl From<usize> for ThreadState {
    fn from(value: usize) -> ThreadState {
        match value {
            0 => ThreadState::Running,
            1 => ThreadState::Parked,
            2 => ThreadState::SafepointRequested,
            3 => ThreadState::ParkedSafepoint,
            4 => ThreadState::Safepoint,
            _ => unreachable!(),
        }
    }
}

impl ThreadState {
    pub fn is_running(&self) -> bool {
        match *self {
            ThreadState::Running | ThreadState::SafepointRequested => true,
            _ => false,
        }
    }

    pub fn is_parked(&self) -> bool {
        match *self {
            ThreadState::Parked | ThreadState::ParkedSafepoint => true,
            _ => false,
        }
    }

    pub fn to_usize(&self) -> usize {
        *self as usize
    }
}

impl Default for ThreadState {
    fn default() -> ThreadState {
        ThreadState::Running
    }
}

pub struct ThreadLocalData {
    tlab_top: AtomicUsize,
    tlab_end: AtomicUsize,
    concurrent_marking: AtomicBool,
    stack_limit: AtomicUsize,
    safepoint_requested: AtomicBool,
    dtn: AtomicUsize,
}

impl ThreadLocalData {
    pub fn new() -> ThreadLocalData {
        ThreadLocalData {
            tlab_top: AtomicUsize::new(0),
            tlab_end: AtomicUsize::new(0),
            concurrent_marking: AtomicBool::new(false),
            stack_limit: AtomicUsize::new(0),
            safepoint_requested: AtomicBool::new(false),
            dtn: AtomicUsize::new(0),
        }
    }

    pub fn tlab_initialize(&self, start: Address, end: Address) {
        assert!(start <= end);

        self.tlab_top.store(start.to_usize(), Ordering::Relaxed);
        self.tlab_end.store(end.to_usize(), Ordering::Relaxed);
    }

    pub fn tlab_rest(&self) -> usize {
        let tlab_top = self.tlab_top.load(Ordering::Relaxed);
        let tlab_end = self.tlab_end.load(Ordering::Relaxed);

        tlab_end - tlab_top
    }

    pub fn tlab_region(&self) -> Region {
        let tlab_top = self.tlab_top.load(Ordering::Relaxed);
        let tlab_end = self.tlab_end.load(Ordering::Relaxed);

        Region::new(tlab_top.into(), tlab_end.into())
    }

    pub fn set_stack_limit(&self, stack_limit: Address) {
        self.stack_limit
            .store(stack_limit.to_usize(), Ordering::Relaxed);
    }

    pub fn tlab_top_offset() -> i32 {
        offset_of!(ThreadLocalData, tlab_top) as i32
    }

    pub fn tlab_end_offset() -> i32 {
        offset_of!(ThreadLocalData, tlab_end) as i32
    }

    pub fn concurrent_marking_offset() -> i32 {
        offset_of!(ThreadLocalData, concurrent_marking) as i32
    }

    pub fn safepoint_requested_offset() -> i32 {
        offset_of!(ThreadLocalData, safepoint_requested) as i32
    }

    pub fn stack_limit(&self) -> Address {
        Address::from(self.stack_limit.load(Ordering::Relaxed))
    }

    pub fn stack_limit_offset() -> i32 {
        offset_of!(ThreadLocalData, stack_limit) as i32
    }

    pub fn dtn_offset() -> i32 {
        offset_of!(ThreadLocalData, dtn) as i32
    }

    pub fn set_safepoint_requested(&self) {
        self.safepoint_requested.store(true, Ordering::Relaxed);
    }

    pub fn clear_safepoint_requested(&self) {
        self.safepoint_requested.store(false, Ordering::Relaxed);
    }
}

pub struct Barrier {
    data: Mutex<BarrierData>,
    cv_wakeup: Condvar,
    cv_notify: Condvar,
}

impl Barrier {
    pub fn new() -> Barrier {
        Barrier {
            data: Mutex::new(BarrierData::new()),
            cv_wakeup: Condvar::new(),
            cv_notify: Condvar::new(),
        }
    }

    pub fn arm(&self) {
        let mut data = self.data.lock();
        assert!(!data.is_armed());
        data.arm();
    }

    pub fn disarm(&self) {
        let mut data = self.data.lock();
        assert!(data.is_armed());
        data.disarm();
        self.cv_wakeup.notify_all();
    }

    pub fn notify_park(&self) {
        let mut data = self.data.lock();
        assert!(data.is_armed());
        data.stopped += 1;
        self.cv_notify.notify_one();
    }

    pub fn wait_in_safepoint(&self) {
        let mut data = self.data.lock();
        assert!(data.is_armed());
        data.stopped += 1;
        self.cv_notify.notify_one();

        while data.is_armed() {
            self.cv_wakeup.wait(&mut data);
        }
    }

    pub fn wait_in_unpark(&self) {
        let mut data = self.data.lock();

        while data.is_armed() {
            self.cv_wakeup.wait(&mut data);
        }
    }

    pub fn wait_until_threads_stopped(&self, threads: usize) {
        let mut data = self.data.lock();
        assert!(data.is_armed());
        while data.stopped < threads {
            self.cv_notify.wait(&mut data);
        }
        assert_eq!(data.stopped, threads);
    }
}

struct BarrierData {
    armed: bool,
    stopped: usize,
}

impl BarrierData {
    pub fn new() -> BarrierData {
        BarrierData {
            armed: false,
            stopped: 0,
        }
    }

    pub fn is_armed(&self) -> bool {
        self.armed
    }

    pub fn arm(&mut self) {
        self.stopped = 0;
        self.armed = true;
    }

    pub fn disarm(&mut self) {
        self.armed = false;
    }
}
<commit_msg>remove StateManager<commit_after>use parking_lot::{Condvar, Mutex};
use std::cell::RefCell;
use std::convert::From;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;

use crate::gc::{tlab, Address, Region, K};
use crate::handle::HandleMemory;
use crate::stack::DoraToNativeInfo;
use crate::vm::{get_vm, VM};

pub const STACK_SIZE: usize = 500 * K;

thread_local! {
    pub static THREAD: RefCell<Arc<DoraThread>> = RefCell::new(DoraThread::main());
}

pub struct Threads {
    pub threads: Mutex<Vec<Arc<DoraThread>>>,
    pub cond_join: Condvar,

    pub next_id: AtomicUsize,

    pub barrier: Barrier,
}

impl Threads {
    pub fn new() -> Threads {
        Threads {
            threads: Mutex::new(Vec::new()),
            cond_join: Condvar::new(),
            next_id: AtomicUsize::new(1),
            barrier: Barrier::new(),
        }
    }

    pub fn attach_current_thread(&self) {
        THREAD.with(|thread| {
            let mut threads = self.threads.lock();
            threads.push(thread.borrow().clone());
        });
    }

    pub fn attach_thread(&self, vm: &VM, thread: Arc<DoraThread>) {
        THREAD.with(|thread_local_thread| {
            thread_local_thread.borrow().park(vm);

            {
                let mut threads = self.threads.lock();
                threads.push(thread);
            }

            thread_local_thread.borrow().unpark(vm);
        });
    }

    pub fn next_id(&self) -> usize {
        self.next_id.fetch_add(1, Ordering::SeqCst)
    }

    pub fn detach_current_thread(&self) {
        let vm = get_vm();

        // Other threads might still be running and perform a GC.
        // Fill the TLAB for them.
        tlab::make_iterable_current(vm);

        THREAD.with(|thread| {
            thread.borrow().park(vm);
            let mut threads = self.threads.lock();
            threads.retain(|elem| !Arc::ptr_eq(elem, &*thread.borrow()));
            self.cond_join.notify_all();
        });
    }

    pub fn join_all(&self) {
        let mut threads = self.threads.lock();

        while threads.len() > 0 {
            self.cond_join.wait(&mut threads);
        }
    }

    pub fn each<F>(&self, mut f: F)
    where
        F: FnMut(&Arc<DoraThread>),
    {
        let threads = self.threads.lock();

        for thread in threads.iter() {
            f(thread)
        }
    }
}

pub struct DoraThread {
    pub id: AtomicUsize,
    pub handles: HandleMemory,
    pub tld: ThreadLocalData,
    pub saved_pc: AtomicUsize,
    pub saved_fp: AtomicUsize,
    pub state: AtomicUsize,
}

unsafe impl Sync for DoraThread {}
unsafe impl Send for DoraThread {}

impl DoraThread {
    pub fn new(vm: &VM) -> Arc<DoraThread> {
        DoraThread::with_id(vm.threads.next_id(), ThreadState::Parked)
    }

    pub fn main() -> Arc<DoraThread> {
        DoraThread::with_id(0, ThreadState::Running)
    }

    fn with_id(id: usize, initial_state: ThreadState) -> Arc<DoraThread> {
        Arc::new(DoraThread {
            id: AtomicUsize::new(id),
            handles: HandleMemory::new(),
            tld: ThreadLocalData::new(),
            saved_pc: AtomicUsize::new(0),
            saved_fp: AtomicUsize::new(0),
            state: AtomicUsize::new(initial_state as usize),
        })
    }

    pub fn id(&self) -> usize {
        self.id.load(Ordering::Relaxed)
    }

    pub fn dtn(&self) -> *const DoraToNativeInfo {
        self.tld.dtn.load(Ordering::Relaxed) as *const _
    }

    pub fn set_dtn(&self, ptr: *const DoraToNativeInfo) {
        self.tld.dtn.store(ptr as usize, Ordering::Relaxed);
    }

    pub fn use_dtn<F, R>(&self, dtn: &mut DoraToNativeInfo, fct: F) -> R
    where
        F: FnOnce() -> R,
    {
        dtn.last = self.dtn();

        self.set_dtn(dtn as *const _);

        let ret = fct();

        self.set_dtn(dtn.last);

        ret
    }

    pub fn push_dtn(&self, dtn: &mut DoraToNativeInfo) {
        dtn.last = self.dtn();
        self.set_dtn(dtn as *const _);
    }

    pub fn pop_dtn(&self) {
        let current_dtn = self.dtn();
        assert!(!current_dtn.is_null());
        let dtn = unsafe { &*current_dtn };
        self.set_dtn(dtn.last);
    }

    pub fn state_relaxed(&self) -> ThreadState {
        self.state.load(Ordering::Relaxed).into()
    }

    pub fn park(&self, vm: &VM) {
        if self
            .state
            .compare_exchange(
                ThreadState::Running as usize,
                ThreadState::Parked as usize,
                Ordering::SeqCst,
                Ordering::SeqCst,
            )
            .is_err()
        {
            self.park_slow(vm);
        }
    }

    pub fn park_slow(&self, vm: &VM) {
        assert!(self
            .state
            .compare_exchange(
                ThreadState::SafepointRequested as usize,
                ThreadState::ParkedSafepoint as usize,
                Ordering::SeqCst,
                Ordering::SeqCst,
            )
            .is_ok());
        vm.threads.barrier.notify_park();
    }

    pub fn unpark(&self, vm: &VM) {
        if self
            .state
            .compare_exchange(
                ThreadState::Parked as usize,
                ThreadState::Running as usize,
                Ordering::SeqCst,
                Ordering::SeqCst,
            )
            .is_err()
        {
            self.unpark_slow(vm);
        }
    }

    pub fn unpark_slow(&self, vm: &VM) {
        loop {
            match self.state.compare_exchange(
                ThreadState::Parked as usize,
                ThreadState::Running as usize,
                Ordering::SeqCst,
                Ordering::SeqCst,
            ) {
                Ok(_) => break,
                Err(state) => {
                    assert_eq!(state, ThreadState::ParkedSafepoint as usize);
                    vm.threads.barrier.wait_in_unpark();
                }
            }
        }
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum ThreadState {
    Running = 0,
    Parked = 1,
    SafepointRequested = 2,
    ParkedSafepoint = 3,
    Safepoint = 4,
}

impl From<usize> for ThreadState {
    fn from(value: usize) -> ThreadState {
        match value {
            0 => ThreadState::Running,
            1 => ThreadState::Parked,
            2 => ThreadState::SafepointRequested,
            3 => ThreadState::ParkedSafepoint,
            4 => ThreadState::Safepoint,
            _ => unreachable!(),
        }
    }
}

impl ThreadState {
    pub fn is_running(&self) -> bool {
        match *self {
            ThreadState::Running | ThreadState::SafepointRequested => true,
            _ => false,
        }
    }

    pub fn is_parked(&self) -> bool {
        match *self {
            ThreadState::Parked | ThreadState::ParkedSafepoint => true,
            _ => false,
        }
    }

    pub fn to_usize(&self) -> usize {
        *self as usize
    }
}

impl Default for ThreadState {
    fn default() -> ThreadState {
        ThreadState::Running
    }
}

pub struct ThreadLocalData {
    tlab_top: AtomicUsize,
    tlab_end: AtomicUsize,
    concurrent_marking: AtomicBool,
    stack_limit: AtomicUsize,
    safepoint_requested: AtomicBool,
    dtn: AtomicUsize,
}

impl ThreadLocalData {
    pub fn new() -> ThreadLocalData {
        ThreadLocalData {
            tlab_top: AtomicUsize::new(0),
            tlab_end: AtomicUsize::new(0),
            concurrent_marking: AtomicBool::new(false),
            stack_limit: AtomicUsize::new(0),
            safepoint_requested: AtomicBool::new(false),
            dtn: AtomicUsize::new(0),
        }
    }

    pub fn tlab_initialize(&self, start: Address, end: Address) {
        assert!(start <= end);

        self.tlab_top.store(start.to_usize(), Ordering::Relaxed);
        self.tlab_end.store(end.to_usize(), Ordering::Relaxed);
    }

    pub fn tlab_rest(&self) -> usize {
        let tlab_top = self.tlab_top.load(Ordering::Relaxed);
        let tlab_end = self.tlab_end.load(Ordering::Relaxed);

        tlab_end - tlab_top
    }

    pub fn tlab_region(&self) -> Region {
        let tlab_top = self.tlab_top.load(Ordering::Relaxed);
        let tlab_end = self.tlab_end.load(Ordering::Relaxed);

        Region::new(tlab_top.into(), tlab_end.into())
    }

    pub fn set_stack_limit(&self, stack_limit: Address) {
        self.stack_limit
            .store(stack_limit.to_usize(), Ordering::Relaxed);
    }

    pub fn tlab_top_offset() -> i32 {
        offset_of!(ThreadLocalData, tlab_top) as i32
    }

    pub fn tlab_end_offset() -> i32 {
        offset_of!(ThreadLocalData, tlab_end) as i32
    }

    pub fn concurrent_marking_offset() -> i32 {
        offset_of!(ThreadLocalData, concurrent_marking) as i32
    }

    pub fn safepoint_requested_offset() -> i32 {
        offset_of!(ThreadLocalData, safepoint_requested) as i32
    }

    pub fn stack_limit(&self) -> Address {
        Address::from(self.stack_limit.load(Ordering::Relaxed))
    }

    pub fn stack_limit_offset() -> i32 {
        offset_of!(ThreadLocalData, stack_limit) as i32
    }

    pub fn dtn_offset() -> i32 {
        offset_of!(ThreadLocalData, dtn) as i32
    }

    pub fn set_safepoint_requested(&self) {
        self.safepoint_requested.store(true, Ordering::Relaxed);
    }

    pub fn clear_safepoint_requested(&self) {
        self.safepoint_requested.store(false, Ordering::Relaxed);
    }
}

pub struct Barrier {
    data: Mutex<BarrierData>,
    cv_wakeup: Condvar,
    cv_notify: Condvar,
}

impl Barrier {
    pub fn new() -> Barrier {
        Barrier {
            data: Mutex::new(BarrierData::new()),
            cv_wakeup: Condvar::new(),
            cv_notify: Condvar::new(),
        }
    }

    pub fn arm(&self) {
        let mut data = self.data.lock();
        assert!(!data.is_armed());
        data.arm();
    }

    pub fn disarm(&self) {
        let mut data = self.data.lock();
        assert!(data.is_armed());
        data.disarm();
        self.cv_wakeup.notify_all();
    }

    pub fn notify_park(&self) {
        let mut data = self.data.lock();
        assert!(data.is_armed());
        data.stopped += 1;
        self.cv_notify.notify_one();
    }

    pub fn wait_in_safepoint(&self) {
        let mut data = self.data.lock();
        assert!(data.is_armed());
        data.stopped += 1;
        self.cv_notify.notify_one();

        while data.is_armed() {
            self.cv_wakeup.wait(&mut data);
        }
    }

    pub fn wait_in_unpark(&self) {
        let mut data = self.data.lock();

        while data.is_armed() {
            self.cv_wakeup.wait(&mut data);
        }
    }

    pub fn wait_until_threads_stopped(&self, threads: usize) {
        let mut data = self.data.lock();
        assert!(data.is_armed());
        while data.stopped < threads {
            self.cv_notify.wait(&mut data);
        }
        assert_eq!(data.stopped, threads);
    }
}

struct BarrierData {
    armed: bool,
    stopped: usize,
}

impl BarrierData {
    pub fn new() -> BarrierData {
        BarrierData {
            armed: false,
            stopped: 0,
        }
    }

    pub fn is_armed(&self) -> bool {
        self.armed
    }

    pub fn arm(&mut self) {
        self.stopped = 0;
        self.armed = true;
    }

    pub fn disarm(&mut self) {
        self.armed = false;
    }
}
<|endoftext|>


<commit_before>use std::default::Default;
use std::path::Path;

use collada;
use gfx;
use gfx::traits::*;
use gfx_texture;

use math::*;
use skeleton::Skeleton;
use transform::Transform;

const MAX_JOINTS: usize = 64;

pub struct SkinnedRenderBatch<R: gfx::Resources, T: Transform> {
    skinning_transforms_buffer: gfx::BufferHandle<R, T>,
    batch: gfx::batch::RefBatch<SkinnedShaderParams<R>>,
}

pub struct SkinnedRenderer<R: gfx::Resources, T: Transform> {
    skeleton: Skeleton, // TODO Should this be a ref? Should this just be the joints?
    render_batches: Vec<SkinnedRenderBatch<R, T>>,
    context: gfx::render::batch::Context<R>,
}

pub trait HasShaderSources<'a> {
    fn vertex_shader_source() -> gfx::ShaderSource<'a>;
    fn fragment_shader_source() -> gfx::ShaderSource<'a>;
}

impl<'a> HasShaderSources<'a> for Matrix4<f32> {
    fn vertex_shader_source() -> gfx::ShaderSource<'a> {
        gfx::ShaderSource {
            glsl_150: Some(include_bytes!("lbs_skinning_150.glslv")),
            .. gfx::ShaderSource::empty()
        }
    }
    fn fragment_shader_source() -> gfx::ShaderSource<'a> {
        gfx::ShaderSource {
            glsl_150: Some(include_bytes!("skinning_150.glslf")),
            .. gfx::ShaderSource::empty()
        }
    }
}

impl<'a> HasShaderSources<'a> for DualQuaternion<f32> {
    fn vertex_shader_source() -> gfx::ShaderSource<'a> {
        gfx::ShaderSource {
            glsl_150: Some(include_bytes!("dlb_skinning_150.glslv")),
            .. gfx::ShaderSource::empty()
        }
    }
    fn fragment_shader_source() -> gfx::ShaderSource<'a> {
        gfx::ShaderSource {
            glsl_150: Some(include_bytes!("skinning_150.glslf")),
            .. gfx::ShaderSource::empty()
        }
    }
}

impl<'a, R: gfx::Resources, T: Transform + HasShaderSources<'a>> SkinnedRenderer<R, T> {

    pub fn from_collada_with_canvas<
        C: gfx::CommandBuffer<R>,
        F: gfx::Factory<R>,
        O: gfx::render::target::Output<R>,
        D: Device<Resources = R, CommandBuffer = C>,
    >(
        canvas: &mut gfx::Canvas<O, D, F>,
        collada_document: collada::document::ColladaDocument,
        texture_paths: Vec<&str>,
    ) -> Result<SkinnedRenderer<R, T>, gfx::ProgramError> {
        SkinnedRenderer::from_collada(&mut canvas.factory, &canvas.device, collada_document, texture_paths)
    }

    pub fn from_collada<
        F: gfx::Factory<R>,
        D: gfx::Device,
    > (
        factory: &mut F,
        device: &D,
        collada_document: collada::document::ColladaDocument,
        texture_paths: Vec<&str>, // TODO - read from the COLLADA document (if available)
    ) -> Result<SkinnedRenderer<R, T>, gfx::ProgramError> {

        let program = {
            let vs = T::vertex_shader_source();
            let fs = T::fragment_shader_source();
            match factory.link_program_source(vs, fs, device.get_capabilities()) {
                Ok(program_handle) => program_handle,
                Err(e) => return Err(e),
            }
        };

        let obj_set = collada_document.get_obj_set().unwrap();

        let skeleton_set = collada_document.get_skeletons().unwrap();
        let skeleton = Skeleton::from_collada(&skeleton_set[0]);

        let mut render_batches = Vec::new();

        let mut context = gfx::render::batch::Context::new();

        for (i, object) in obj_set.objects.iter().enumerate().take(6) {

            let mut vertex_data: Vec<SkinnedVertex> = Vec::new();
            let mut index_data: Vec<u32> = Vec::new();

            get_vertex_index_data(&object, &mut vertex_data, &mut index_data);

            let mesh = factory.create_mesh(vertex_data.as_slice());

            let state = gfx::DrawState::new().depth(gfx::state::Comparison::LessEqual, true);

            let slice = factory
                .create_buffer_index::<u32>(index_data.as_slice())
                .to_slice(gfx::PrimitiveType::TriangleList);

            let skinning_transforms_buffer = factory.create_buffer::<T>(MAX_JOINTS, gfx::BufferUsage::Dynamic);

            let texture = gfx_texture::Texture::from_path(
                factory,
                &Path::new(&texture_paths[i]),
                &gfx_texture::Settings::new()
            ).unwrap();

            let sampler = factory.create_sampler(
                gfx::tex::SamplerInfo::new(
                    gfx::tex::FilterMethod::Trilinear,
                    gfx::tex::WrapMode::Clamp
                )
            );

            let shader_params = SkinnedShaderParams {
                u_model_view_proj: mat4_id(),
                u_model_view: mat4_id(),
                u_skinning_transforms: skinning_transforms_buffer.raw().clone(),
                u_texture: (texture.handle(), Some(sampler)),
            };

            let batch: gfx::batch::RefBatch<SkinnedShaderParams<R>> = context.make_batch(&program, shader_params, &mesh, slice, &state).unwrap();

            render_batches.push(SkinnedRenderBatch {
                batch: batch,
                skinning_transforms_buffer: skinning_transforms_buffer,
            });
        }

        Ok(SkinnedRenderer {
            render_batches: render_batches,
            skeleton: skeleton.clone(),
            context: context,
        })
    }

    pub fn render_canvas<
        C: gfx::CommandBuffer<R>,
        F: gfx::Factory<R>,
        O: gfx::render::target::Output<R>,
        D: Device<Resources = R, CommandBuffer = C>,
    >(
        &mut self,
        canvas: &mut gfx::Canvas<O, D, F>,
        view: [[f32; 4]; 4],
        projection: [[f32; 4]; 4],
        joint_poses: &[T]
    ) {
        self.render(&mut canvas.renderer, &mut canvas.factory, &canvas.output, view, projection, joint_poses);
    }

    pub fn render<
        C: gfx::CommandBuffer<R>,
        F: gfx::Factory<R>,
        O: gfx::render::target::Output<R>,
    >(
        &mut self,
        renderer: &mut gfx::Renderer<R, C>,
        factory: &mut F,
        output: &O,
        view: [[f32; 4]; 4],
        projection: [[f32; 4]; 4],
        joint_poses: &[T]
    ) {

        let skinning_transforms = self.calculate_skinning_transforms(&joint_poses);

        for material in self.render_batches.iter_mut() {
            material.batch.params.u_model_view = view;
            material.batch.params.u_model_view_proj = projection;

            // FIXME -- should all be able to share the same buffer
            factory.update_buffer(&material.skinning_transforms_buffer, &skinning_transforms[..], 0);

            renderer.draw(&(&material.batch, &self.context), output).unwrap();
        }
    }

    ///
    /// TODO - don't allocate a new vector
    ///
    pub fn calculate_skinning_transforms(&self, global_poses: &[T]) -> Vec<T> {
        self.skeleton.joints.iter().enumerate().map(|(i, joint)| {
            // TODO avoid conversion...
            global_poses[i].concat(T::from_matrix(joint.inverse_bind_pose))
        }).collect()
    }
}

#[shader_param]
struct SkinnedShaderParams<R: gfx::Resources> {
    u_model_view_proj: [[f32; 4]; 4],
    u_model_view: [[f32; 4]; 4],
    u_skinning_transforms: gfx::RawBufferHandle<R>,
    u_texture: gfx::shade::TextureParam<R>,
}

#[vertex_format]
#[derive(Copy)]
#[derive(Clone)]
#[derive(Debug)]
struct SkinnedVertex {
    pos: [f32; 3],
    normal: [f32; 3],
    uv: [f32; 2],
    joint_indices: [i32; 4],
    joint_weights: [f32; 4], // TODO last weight is redundant
}

impl Default for SkinnedVertex {
    fn default() -> SkinnedVertex {
        SkinnedVertex {
            pos: [0.0; 3],
            normal: [0.0; 3],
            uv: [0.0; 2],
            joint_indices: [0; 4],
            joint_weights: [0.0; 4],
        }
    }
}

fn vtn_to_vertex(a: collada::VTNIndex, obj: &collada::Object) -> SkinnedVertex
{
    let mut vertex: SkinnedVertex = Default::default();
    let position = obj.vertices[a.0];

    vertex.pos = [position.x as f32, position.y as f32, position.z as f32];

    if obj.joint_weights.len() == obj.vertices.len() {
        let weights = obj.joint_weights[a.0];
        vertex.joint_weights = weights.weights;
        vertex.joint_indices = [
            weights.joints[0] as i32,
            weights.joints[1] as i32,
            weights.joints[2] as i32,
            weights.joints[3] as i32,
        ];
    }

    if let Some(uv) = a.1 {
        let uv = obj.tex_vertices[uv];
        vertex.uv = [uv.x as f32, uv.y as f32];
    }

    if let Some(normal) = a.2 {
        let normal = obj.normals[normal];
        vertex.normal = [normal.x as f32, normal.y as f32, normal.z as f32];
    }

    vertex
}

fn get_vertex_index_data(obj: &collada::Object, vertex_data: &mut Vec<SkinnedVertex>, index_data: &mut Vec<u32>) {
    for geom in obj.geometry.iter() {
        let mut i = vertex_data.len() as u32;
        let mut uvs: u32 = 0;
        let mut normals: u32 = 0;
        {
            let mut add = |a: collada::VTNIndex| {
                if let Some(_) = a.1 { uvs += 1; }
                if let Some(_) = a.2 { normals += 1; }
                vertex_data.push(vtn_to_vertex(a, obj));
                index_data.push(i);
                i += 1;
            };
            for shape in geom.shapes.iter() {
                match shape {
                    &collada::Shape::Triangle(a, b, c) => {
                        add(a);
                        add(b);
                        add(c);
                    }
                    _ => {}
                }
            }
        }
    }
}
<commit_msg>Update to latest gfx<commit_after>use std::default::Default;
use std::path::Path;

use collada;
use gfx;
use gfx::traits::*;
use gfx_texture;

use math::*;
use skeleton::Skeleton;
use transform::Transform;

const MAX_JOINTS: usize = 64;

pub struct SkinnedRenderBatch<R: gfx::Resources, T: Transform> {
    skinning_transforms_buffer: gfx::handle::Buffer<R, T>,
    batch: gfx::batch::RefBatch<SkinnedShaderParams<R>>,
}

pub struct SkinnedRenderer<R: gfx::Resources, T: Transform> {
    skeleton: Skeleton, // TODO Should this be a ref? Should this just be the joints?
    render_batches: Vec<SkinnedRenderBatch<R, T>>,
    context: gfx::render::batch::Context<R>,
}

pub trait HasShaderSources<'a> {
    fn vertex_shader_source() -> gfx::ShaderSource<'a>;
    fn fragment_shader_source() -> gfx::ShaderSource<'a>;
}

impl<'a> HasShaderSources<'a> for Matrix4<f32> {
    fn vertex_shader_source() -> gfx::ShaderSource<'a> {
        gfx::ShaderSource {
            glsl_150: Some(include_bytes!("lbs_skinning_150.glslv")),
            .. gfx::ShaderSource::empty()
        }
    }
    fn fragment_shader_source() -> gfx::ShaderSource<'a> {
        gfx::ShaderSource {
            glsl_150: Some(include_bytes!("skinning_150.glslf")),
            .. gfx::ShaderSource::empty()
        }
    }
}

impl<'a> HasShaderSources<'a> for DualQuaternion<f32> {
    fn vertex_shader_source() -> gfx::ShaderSource<'a> {
        gfx::ShaderSource {
            glsl_150: Some(include_bytes!("dlb_skinning_150.glslv")),
            .. gfx::ShaderSource::empty()
        }
    }
    fn fragment_shader_source() -> gfx::ShaderSource<'a> {
        gfx::ShaderSource {
            glsl_150: Some(include_bytes!("skinning_150.glslf")),
            .. gfx::ShaderSource::empty()
        }
    }
}

impl<'a, R: gfx::Resources, T: Transform + HasShaderSources<'a>> SkinnedRenderer<R, T> {

    pub fn from_collada_with_canvas<
        C: gfx::CommandBuffer<R>,
        F: gfx::Factory<R>,
        O: gfx::render::target::Output<R>,
        D: Device<Resources = R, CommandBuffer = C>,
    >(
        canvas: &mut gfx::Canvas<O, D, F>,
        collada_document: collada::document::ColladaDocument,
        texture_paths: Vec<&str>,
    ) -> Result<SkinnedRenderer<R, T>, gfx::ProgramError> {
        SkinnedRenderer::from_collada(&mut canvas.factory, &canvas.device, collada_document, texture_paths)
    }

    pub fn from_collada<
        F: gfx::Factory<R>,
        D: gfx::Device,
    > (
        factory: &mut F,
        device: &D,
        collada_document: collada::document::ColladaDocument,
        texture_paths: Vec<&str>, // TODO - read from the COLLADA document (if available)
    ) -> Result<SkinnedRenderer<R, T>, gfx::ProgramError> {

        let program = {
            let vs = T::vertex_shader_source();
            let fs = T::fragment_shader_source();
            match factory.link_program_source(vs, fs, device.get_capabilities()) {
                Ok(program_handle) => program_handle,
                Err(e) => return Err(e),
            }
        };

        let obj_set = collada_document.get_obj_set().unwrap();

        let skeleton_set = collada_document.get_skeletons().unwrap();
        let skeleton = Skeleton::from_collada(&skeleton_set[0]);

        let mut render_batches = Vec::new();

        let mut context = gfx::render::batch::Context::new();

        for (i, object) in obj_set.objects.iter().enumerate().take(6) {

            let mut vertex_data: Vec<SkinnedVertex> = Vec::new();
            let mut index_data: Vec<u32> = Vec::new();

            get_vertex_index_data(&object, &mut vertex_data, &mut index_data);

            let mesh = factory.create_mesh(vertex_data.as_slice());

            let state = gfx::DrawState::new().depth(gfx::state::Comparison::LessEqual, true);

            let slice = factory
                .create_buffer_index::<u32>(index_data.as_slice())
                .to_slice(gfx::PrimitiveType::TriangleList);

            let skinning_transforms_buffer = factory.create_buffer::<T>(MAX_JOINTS, gfx::BufferUsage::Dynamic);

            let texture = gfx_texture::Texture::from_path(
                factory,
                &Path::new(&texture_paths[i]),
                &gfx_texture::Settings::new()
            ).unwrap();

            let sampler = factory.create_sampler(
                gfx::tex::SamplerInfo::new(
                    gfx::tex::FilterMethod::Trilinear,
                    gfx::tex::WrapMode::Clamp
                )
            );

            let shader_params = SkinnedShaderParams {
                u_model_view_proj: mat4_id(),
                u_model_view: mat4_id(),
                u_skinning_transforms: skinning_transforms_buffer.raw().clone(),
                u_texture: (texture.handle(), Some(sampler)),
            };

            let batch: gfx::batch::RefBatch<SkinnedShaderParams<R>> = context.make_batch(&program, shader_params, &mesh, slice, &state).unwrap();

            render_batches.push(SkinnedRenderBatch {
                batch: batch,
                skinning_transforms_buffer: skinning_transforms_buffer,
            });
        }

        Ok(SkinnedRenderer {
            render_batches: render_batches,
            skeleton: skeleton.clone(),
            context: context,
        })
    }

    pub fn render_canvas<
        C: gfx::CommandBuffer<R>,
        F: gfx::Factory<R>,
        O: gfx::render::target::Output<R>,
        D: Device<Resources = R, CommandBuffer = C>,
    >(
        &mut self,
        canvas: &mut gfx::Canvas<O, D, F>,
        view: [[f32; 4]; 4],
        projection: [[f32; 4]; 4],
        joint_poses: &[T]
    ) {
        self.render(&mut canvas.renderer, &mut canvas.factory, &canvas.output, view, projection, joint_poses);
    }

    pub fn render<
        C: gfx::CommandBuffer<R>,
        F: gfx::Factory<R>,
        O: gfx::render::target::Output<R>,
    >(
        &mut self,
        renderer: &mut gfx::Renderer<R, C>,
        factory: &mut F,
        output: &O,
        view: [[f32; 4]; 4],
        projection: [[f32; 4]; 4],
        joint_poses: &[T]
    ) {

        let skinning_transforms = self.calculate_skinning_transforms(&joint_poses);

        for material in self.render_batches.iter_mut() {
            material.batch.params.u_model_view = view;
            material.batch.params.u_model_view_proj = projection;

            // FIXME -- should all be able to share the same buffer
            factory.update_buffer(&material.skinning_transforms_buffer, &skinning_transforms[..], 0);

            renderer.draw(&(&material.batch, &self.context), output).unwrap();
        }
    }

    ///
    /// TODO - don't allocate a new vector
    ///
    pub fn calculate_skinning_transforms(&self, global_poses: &[T]) -> Vec<T> {
        self.skeleton.joints.iter().enumerate().map(|(i, joint)| {
            // TODO avoid conversion...
            global_poses[i].concat(T::from_matrix(joint.inverse_bind_pose))
        }).collect()
    }
}

#[shader_param]
struct SkinnedShaderParams<R: gfx::Resources> {
    u_model_view_proj: [[f32; 4]; 4],
    u_model_view: [[f32; 4]; 4],
    u_skinning_transforms: gfx::handle::RawBuffer<R>,
    u_texture: gfx::shade::TextureParam<R>,
}

#[vertex_format]
#[derive(Copy)]
#[derive(Clone)]
#[derive(Debug)]
struct SkinnedVertex {
    pos: [f32; 3],
    normal: [f32; 3],
    uv: [f32; 2],
    joint_indices: [i32; 4],
    joint_weights: [f32; 4], // TODO last weight is redundant
}

impl Default for SkinnedVertex {
    fn default() -> SkinnedVertex {
        SkinnedVertex {
            pos: [0.0; 3],
            normal: [0.0; 3],
            uv: [0.0; 2],
            joint_indices: [0; 4],
            joint_weights: [0.0; 4],
        }
    }
}

fn vtn_to_vertex(a: collada::VTNIndex, obj: &collada::Object) -> SkinnedVertex
{
    let mut vertex: SkinnedVertex = Default::default();
    let position = obj.vertices[a.0];

    vertex.pos = [position.x as f32, position.y as f32, position.z as f32];

    if obj.joint_weights.len() == obj.vertices.len() {
        let weights = obj.joint_weights[a.0];
        vertex.joint_weights = weights.weights;
        vertex.joint_indices = [
            weights.joints[0] as i32,
            weights.joints[1] as i32,
            weights.joints[2] as i32,
            weights.joints[3] as i32,
        ];
    }

    if let Some(uv) = a.1 {
        let uv = obj.tex_vertices[uv];
        vertex.uv = [uv.x as f32, uv.y as f32];
    }

    if let Some(normal) = a.2 {
        let normal = obj.normals[normal];
        vertex.normal = [normal.x as f32, normal.y as f32, normal.z as f32];
    }

    vertex
}

fn get_vertex_index_data(obj: &collada::Object, vertex_data: &mut Vec<SkinnedVertex>, index_data: &mut Vec<u32>) {
    for geom in obj.geometry.iter() {
        let mut i = vertex_data.len() as u32;
        let mut uvs: u32 = 0;
        let mut normals: u32 = 0;
        {
            let mut add = |a: collada::VTNIndex| {
                if let Some(_) = a.1 { uvs += 1; }
                if let Some(_) = a.2 { normals += 1; }
                vertex_data.push(vtn_to_vertex(a, obj));
                index_data.push(i);
                i += 1;
            };
            for shape in geom.shapes.iter() {
                match shape {
                    &collada::Shape::Triangle(a, b, c) => {
                        add(a);
                        add(b);
                        add(c);
                    }
                    _ => {}
                }
            }
        }
    }
}
<|endoftext|>