<commit_before>use super::{
    heap::{Closure, Data, Heap, Pointer},
    Vm,
};
use crate::{
    compiler::{
        hir_to_lir::HirToLir,
        lir::{Instruction, Lir},
    },
    database::Database,
    module::{Module, ModuleDb, ModuleKind},
};
use itertools::Itertools;

pub trait UseProvider {
    fn use_module(&self, module: Module) -> Result<UseResult, String>;
}
pub enum UseResult {
    Asset(Vec<u8>),
    Code(Lir),
}

pub struct DbUseProvider<'a> {
    pub db: &'a Database,
}
impl<'a> UseProvider for DbUseProvider<'a> {
    fn use_module(&self, module: Module) -> Result<UseResult, String> {
        match module.kind {
            ModuleKind::Asset => match self.db.get_module_content(module.clone()) {
                Some(bytes) => Ok(UseResult::Asset((*bytes).clone())),
                None => Err(format!("use couldn't import the asset module `{}`", module)),
            },
            ModuleKind::Code => match self.db.lir(module.clone()) {
                Some(lir) => Ok(UseResult::Code((*lir).clone())),
                None => Err(format!("use couldn't import the code module `{}`", module)),
            },
        }
    }
}

impl Vm {
    pub fn use_module<U: UseProvider>(
        &mut self,
        use_provider: &U,
        current_module: Module,
        relative_path: Pointer,
    ) -> Result<(), String> {
        let target = UsePath::parse(&self.heap, relative_path)?;
        let module = target.resolve_relative_to(current_module)?;

        match use_provider.use_module(module.clone())? {
            UseResult::Asset(bytes) => {
                let bytes = bytes
                    .iter()
                    .map(|byte| self.heap.create_int((*byte).into()))
                    .collect_vec();
                let list = self.heap.create_list(bytes);
                self.data_stack.push(list);
            }
            UseResult::Code(lir) => {
                let module_closure = Closure::of_module_lir(module, lir);
                let address = self.heap.create_closure(module_closure);
                self.data_stack.push(address);
                self.run_instruction(use_provider, Instruction::Call { num_args: 0 });
            }
        }

        Ok(())
    }
}

struct UsePath {
    parent_navigations: usize,
    path: String,
}
impl UsePath {
    const PARENT_NAVIGATION_CHAR: char = '.';

    fn parse(heap: &Heap, path: Pointer) -> Result<Self, String> {
        let path = match &heap.get(path).data {
            Data::Text(path) => path.value.clone(),
            _ => return Err("the path has to be a text".to_string()),
        };
        let mut path = path.as_str();
        let parent_navigations = {
            let mut navigations = 0;
            while path.starts_with(UsePath::PARENT_NAVIGATION_CHAR) {
                navigations += 1;
                path = &path[UsePath::PARENT_NAVIGATION_CHAR.len_utf8()..];
            }
            match navigations {
                0 => return Err("the target must start with at least one dot".to_string()),
                i => i - 1, // two dots means one parent navigation
            }
        };
        let path = {
            if !path.chars().all(|c| c.is_ascii_alphanumeric() || c == '.') {
                return Err("the target name can only contain letters and dots".to_string());
            }
            path.to_string()
        };
        Ok(UsePath {
            parent_navigations,
            path,
        })
    }

    fn resolve_relative_to(&self, current_module: Module) -> Result<Module, String> {
        let kind = if self.path.contains('.') {
            ModuleKind::Asset
        } else {
            ModuleKind::Code
        };

        let mut path = current_module.path;
        for _ in 0..self.parent_navigations {
            if path.pop() == None {
                return Err("too many parent navigations".to_string());
            }
        }
        path.push(self.path.to_string());

        Ok(Module {
            package: current_module.package,
            path: path.clone(),
            kind,
        })
    }
}
<commit_msg>Use try_into instead of match<commit_after>use super::{
    heap::{Closure, Heap, Pointer, Text},
    Vm,
};
use crate::{
    compiler::{
        hir_to_lir::HirToLir,
        lir::{Instruction, Lir},
    },
    database::Database,
    module::{Module, ModuleDb, ModuleKind},
};
use itertools::Itertools;

pub trait UseProvider {
    fn use_module(&self, module: Module) -> Result<UseResult, String>;
}
pub enum UseResult {
    Asset(Vec<u8>),
    Code(Lir),
}

pub struct DbUseProvider<'a> {
    pub db: &'a Database,
}
impl<'a> UseProvider for DbUseProvider<'a> {
    fn use_module(&self, module: Module) -> Result<UseResult, String> {
        match module.kind {
            ModuleKind::Asset => match self.db.get_module_content(module.clone()) {
                Some(bytes) => Ok(UseResult::Asset((*bytes).clone())),
                None => Err(format!("use couldn't import the asset module `{}`", module)),
            },
            ModuleKind::Code => match self.db.lir(module.clone()) {
                Some(lir) => Ok(UseResult::Code((*lir).clone())),
                None => Err(format!("use couldn't import the code module `{}`", module)),
            },
        }
    }
}

impl Vm {
    pub fn use_module<U: UseProvider>(
        &mut self,
        use_provider: &U,
        current_module: Module,
        relative_path: Pointer,
    ) -> Result<(), String> {
        let target = UsePath::parse(&self.heap, relative_path)?;
        let module = target.resolve_relative_to(current_module)?;

        match use_provider.use_module(module.clone())? {
            UseResult::Asset(bytes) => {
                let bytes = bytes
                    .iter()
                    .map(|byte| self.heap.create_int((*byte).into()))
                    .collect_vec();
                let list = self.heap.create_list(bytes);
                self.data_stack.push(list);
            }
            UseResult::Code(lir) => {
                let module_closure = Closure::of_module_lir(module, lir);
                let address = self.heap.create_closure(module_closure);
                self.data_stack.push(address);
                self.run_instruction(use_provider, Instruction::Call { num_args: 0 });
            }
        }

        Ok(())
    }
}

struct UsePath {
    parent_navigations: usize,
    path: String,
}
impl UsePath {
    const PARENT_NAVIGATION_CHAR: char = '.';

    fn parse(heap: &Heap, path: Pointer) -> Result<Self, String> {
        let path: Text = heap
            .get(path)
            .data
            .clone()
            .try_into()
            .map_err(|_| "the path has to be a text".to_string())?;
        let mut path = path.value.as_str();
        let parent_navigations = {
            let mut navigations = 0;
            while path.starts_with(UsePath::PARENT_NAVIGATION_CHAR) {
                navigations += 1;
                path = &path[UsePath::PARENT_NAVIGATION_CHAR.len_utf8()..];
            }
            match navigations {
                0 => return Err("the target must start with at least one dot".to_string()),
                i => i - 1, // two dots means one parent navigation
            }
        };
        let path = {
            if !path.chars().all(|c| c.is_ascii_alphanumeric() || c == '.') {
                return Err("the target name can only contain letters and dots".to_string());
            }
            path.to_string()
        };
        Ok(UsePath {
            parent_navigations,
            path,
        })
    }

    fn resolve_relative_to(&self, current_module: Module) -> Result<Module, String> {
        let kind = if self.path.contains('.') {
            ModuleKind::Asset
        } else {
            ModuleKind::Code
        };

        let mut path = current_module.path;
        for _ in 0..self.parent_navigations {
            if path.pop() == None {
                return Err("too many parent navigations".to_string());
            }
        }
        path.push(self.path.to_string());

        Ok(Module {
            package: current_module.package,
            path: path.clone(),
            kind,
        })
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016, 2018 vergen developers
//
// 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. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

//! Output types
use crate::constants::*;
use chrono::Utc;
use std::collections::HashMap;
use std::env;
use std::error::Error;
use std::process::Command;

//pub mod codegen;
pub mod envvar;

pub fn generate_build_info(
  flags: ConstantsFlags,
) -> Result<HashMap<VergenKey, String>, Box<dyn Error>> {
  let mut build_info = HashMap::new();
  let now = Utc::now();

  if flags.contains(ConstantsFlags::BUILD_TIMESTAMP) {
    build_info.insert(VergenKey::BuildTimestamp, now.to_rfc3339());
  }

  if flags.contains(ConstantsFlags::BUILD_DATE) {
    build_info
      .insert(VergenKey::BuildDate, now.format("%Y-%m-%d").to_string());
  }

  if flags.contains(ConstantsFlags::SHA) {
    let sha = run_command(Command::new("git").args(&["rev-parse", "HEAD"]));
    build_info.insert(VergenKey::Sha, sha);
  }

  if flags.contains(ConstantsFlags::SHA_SHORT) {
    let sha =
      run_command(Command::new("git").args(&["rev-parse", "--short", "HEAD"]));
    build_info.insert(VergenKey::ShortSha, sha);
  }

  if flags.contains(ConstantsFlags::COMMIT_DATE) {
    let commit_date = run_command(Command::new("git").args(&[
      "log",
      "--pretty=format:'%ad'",
      "-n1",
      "--date=short",
    ]));
    build_info.insert(
      VergenKey::CommitDate,
      commit_date.trim_matches('\'').to_string(),
    );
  }

  if flags.contains(ConstantsFlags::TARGET_TRIPLE) {
    let target_triple =
      env::var("TARGET").unwrap_or_else(|_| "UNKNOWN".to_string());
    build_info.insert(VergenKey::TargetTriple, target_triple);
  }

  if flags.contains(ConstantsFlags::SEMVER) {
    let describe = run_command(Command::new("git").args(&["describe"]));

    let semver = if describe.eq_ignore_ascii_case(&"UNKNOWN") {
      env::var("CARGO_PKG_VERSION")?
    } else {
      describe
    };
    build_info.insert(VergenKey::Semver, semver);
  } else if flags.contains(ConstantsFlags::SEMVER_FROM_CARGO_PKG) {
    build_info.insert(VergenKey::Semver, env::var("CARGO_PKG_VERSION")?);
  }

  if flags.contains(ConstantsFlags::SEMVER_LIGHTWEIGHT) {
    let describe =
      run_command(Command::new("git").args(&["describe", "--tags"]));

    let semver = if describe.eq_ignore_ascii_case(&"UNKNOWN") {
      env::var("CARGO_PKG_VERSION")?
    } else {
      describe
    };
    build_info.insert(VergenKey::SemverLightweight, semver);
  }

  Ok(build_info)
}

fn run_command(command: &mut Command) -> String {
  if let Ok(o) = command.output() {
    if o.status.success() {
      return String::from_utf8_lossy(&o.stdout).trim().to_owned();
    }
  }
  "UNKNOWN".to_owned()
}

/// Build information keys.
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum VergenKey {
  /// The build timestamp. (VERGEN_BUILD_TIMESTAMP)
  BuildTimestamp,
  /// The build date. (VERGEN_BUILD_DATE)
  BuildDate,
  /// The latest commit SHA. (VERGEN_SHA)
  Sha,
  /// The latest commit short SHA. (VERGEN_SHA_SHORT)
  ShortSha,
  /// The commit date. (VERGEN_COMMIT_DATE).
  CommitDate,
  /// The target triple. (VERGEN_TARGET_TRIPLE)
  TargetTriple,
  /// The semver version from the last git tag. (VERGEN_SEMVER)
  Semver,
  /// The semver version from the last git tag, including lightweight.
  /// (VERGEN_SEMVER_LIGHTWEIGHT)
  SemverLightweight,
}

impl VergenKey {
  /// Get the name for the given key.
  pub fn name(self) -> &'static str {
    match self {
      VergenKey::BuildTimestamp => BUILD_TIMESTAMP_NAME,
      VergenKey::BuildDate => BUILD_DATE_NAME,
      VergenKey::Sha => SHA_NAME,
      VergenKey::ShortSha => SHA_SHORT_NAME,
      VergenKey::CommitDate => COMMIT_DATE_NAME,
      VergenKey::TargetTriple => TARGET_TRIPLE_NAME,
      VergenKey::Semver => SEMVER_NAME,
      VergenKey::SemverLightweight => SEMVER_TAGS_NAME,
    }
  }
}

#[cfg(test)]
mod test {}
<commit_msg>vergen: Fix clippy errors<commit_after>// Copyright (c) 2016, 2018 vergen developers
//
// 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. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

//! Output types
use crate::constants::*;
use chrono::Utc;
use std::collections::HashMap;
use std::env;
use std::error::Error;
use std::process::Command;

//pub mod codegen;
pub mod envvar;

pub fn generate_build_info(
  flags: ConstantsFlags,
) -> Result<HashMap<VergenKey, String>, Box<dyn Error>> {
  let mut build_info = HashMap::new();
  let now = Utc::now();

  if flags.contains(ConstantsFlags::BUILD_TIMESTAMP) {
    build_info.insert(VergenKey::BuildTimestamp, now.to_rfc3339());
  }

  if flags.contains(ConstantsFlags::BUILD_DATE) {
    build_info
      .insert(VergenKey::BuildDate, now.format("%Y-%m-%d").to_string());
  }

  if flags.contains(ConstantsFlags::SHA) {
    let sha = run_command(Command::new("git").args(&["rev-parse", "HEAD"]));
    build_info.insert(VergenKey::Sha, sha);
  }

  if flags.contains(ConstantsFlags::SHA_SHORT) {
    let sha =
      run_command(Command::new("git").args(&["rev-parse", "--short", "HEAD"]));
    build_info.insert(VergenKey::ShortSha, sha);
  }

  if flags.contains(ConstantsFlags::COMMIT_DATE) {
    let commit_date = run_command(Command::new("git").args(&[
      "log",
      "--pretty=format:'%ad'",
      "-n1",
      "--date=short",
    ]));
    build_info.insert(
      VergenKey::CommitDate,
      commit_date.trim_matches('\'').to_string(),
    );
  }

  if flags.contains(ConstantsFlags::TARGET_TRIPLE) {
    let target_triple =
      env::var("TARGET").unwrap_or_else(|_| "UNKNOWN".to_string());
    build_info.insert(VergenKey::TargetTriple, target_triple);
  }

  if flags.contains(ConstantsFlags::SEMVER) {
    let describe = run_command(Command::new("git").args(&["describe"]));

    let semver = if describe.eq_ignore_ascii_case("UNKNOWN") {
      env::var("CARGO_PKG_VERSION")?
    } else {
      describe
    };
    build_info.insert(VergenKey::Semver, semver);
  } else if flags.contains(ConstantsFlags::SEMVER_FROM_CARGO_PKG) {
    build_info.insert(VergenKey::Semver, env::var("CARGO_PKG_VERSION")?);
  }

  if flags.contains(ConstantsFlags::SEMVER_LIGHTWEIGHT) {
    let describe =
      run_command(Command::new("git").args(&["describe", "--tags"]));

    let semver = if describe.eq_ignore_ascii_case("UNKNOWN") {
      env::var("CARGO_PKG_VERSION")?
    } else {
      describe
    };
    build_info.insert(VergenKey::SemverLightweight, semver);
  }

  Ok(build_info)
}

fn run_command(command: &mut Command) -> String {
  if let Ok(o) = command.output() {
    if o.status.success() {
      return String::from_utf8_lossy(&o.stdout).trim().to_owned();
    }
  }
  "UNKNOWN".to_owned()
}

/// Build information keys.
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum VergenKey {
  /// The build timestamp. (VERGEN_BUILD_TIMESTAMP)
  BuildTimestamp,
  /// The build date. (VERGEN_BUILD_DATE)
  BuildDate,
  /// The latest commit SHA. (VERGEN_SHA)
  Sha,
  /// The latest commit short SHA. (VERGEN_SHA_SHORT)
  ShortSha,
  /// The commit date. (VERGEN_COMMIT_DATE).
  CommitDate,
  /// The target triple. (VERGEN_TARGET_TRIPLE)
  TargetTriple,
  /// The semver version from the last git tag. (VERGEN_SEMVER)
  Semver,
  /// The semver version from the last git tag, including lightweight.
  /// (VERGEN_SEMVER_LIGHTWEIGHT)
  SemverLightweight,
}

impl VergenKey {
  /// Get the name for the given key.
  pub fn name(self) -> &'static str {
    match self {
      VergenKey::BuildTimestamp => BUILD_TIMESTAMP_NAME,
      VergenKey::BuildDate => BUILD_DATE_NAME,
      VergenKey::Sha => SHA_NAME,
      VergenKey::ShortSha => SHA_SHORT_NAME,
      VergenKey::CommitDate => COMMIT_DATE_NAME,
      VergenKey::TargetTriple => TARGET_TRIPLE_NAME,
      VergenKey::Semver => SEMVER_NAME,
      VergenKey::SemverLightweight => SEMVER_TAGS_NAME,
    }
  }
}

#[cfg(test)]
mod test {}
<|endoftext|>


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

use api::{AsyncBlobImageRasterizer, BlobImageRequest, BlobImageParams, BlobImageResult};
use api::{DocumentId, PipelineId, ApiMsg, FrameMsg, ResourceUpdate, Epoch};
use api::{BuiltDisplayList, ColorF, LayoutSize};
use api::channel::MsgSender;
use frame_builder::{FrameBuilderConfig, FrameBuilder};
use clip_scroll_tree::ClipScrollTree;
use display_list_flattener::DisplayListFlattener;
use internal_types::{FastHashMap, FastHashSet};
use resource_cache::FontInstanceMap;
use render_backend::DocumentView;
use renderer::{PipelineInfo, SceneBuilderHooks};
use scene::Scene;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::mem::replace;
use time::precise_time_ns;

/// Represents the work associated to a transaction before scene building.
pub struct Transaction {
    pub document_id: DocumentId,
    pub display_list_updates: Vec<DisplayListUpdate>,
    pub removed_pipelines: Vec<PipelineId>,
    pub epoch_updates: Vec<(PipelineId, Epoch)>,
    pub request_scene_build: Option<SceneRequest>,
    pub blob_requests: Vec<BlobImageParams>,
    pub blob_rasterizer: Option<Box<AsyncBlobImageRasterizer>>,
    pub resource_updates: Vec<ResourceUpdate>,
    pub frame_ops: Vec<FrameMsg>,
    pub set_root_pipeline: Option<PipelineId>,
    pub render: bool,
}

impl Transaction {
    pub fn can_skip_scene_builder(&self) -> bool {
        self.request_scene_build.is_none() &&
            self.display_list_updates.is_empty() &&
            self.epoch_updates.is_empty() &&
            self.removed_pipelines.is_empty() &&
            self.blob_requests.is_empty() &&
            self.set_root_pipeline.is_none()
    }

    pub fn should_build_scene(&self) -> bool {
        !self.display_list_updates.is_empty() ||
            self.set_root_pipeline.is_some()
    }
}

/// Represent the remaining work associated to a transaction after the scene building
/// phase as well as the result of scene building itself if applicable.
pub struct BuiltTransaction {
    pub document_id: DocumentId,
    pub built_scene: Option<BuiltScene>,
    pub resource_updates: Vec<ResourceUpdate>,
    pub rasterized_blobs: Vec<(BlobImageRequest, BlobImageResult)>,
    pub blob_rasterizer: Option<Box<AsyncBlobImageRasterizer>>,
    pub frame_ops: Vec<FrameMsg>,
    pub removed_pipelines: Vec<PipelineId>,
    pub scene_build_start_time: u64,
    pub scene_build_end_time: u64,
    pub render: bool,
}

pub struct DisplayListUpdate {
    pub pipeline_id: PipelineId,
    pub epoch: Epoch,
    pub built_display_list: BuiltDisplayList,
    pub background: Option<ColorF>,
    pub viewport_size: LayoutSize,
    pub content_size: LayoutSize,
}

/// Contains the render backend data needed to build a scene.
pub struct SceneRequest {
    pub view: DocumentView,
    pub font_instances: FontInstanceMap,
    pub output_pipelines: FastHashSet<PipelineId>,
    pub scene_id: u64,
}

/// For the replay feature.
pub struct LoadScene {
    pub document_id: DocumentId,
    pub scene: Scene,
    pub scene_id: u64,
    pub output_pipelines: FastHashSet<PipelineId>,
    pub font_instances: FontInstanceMap,
    pub view: DocumentView,
    pub config: FrameBuilderConfig,
    pub generate_frame: bool,
}

pub struct BuiltScene {
    pub scene: Scene,
    pub frame_builder: FrameBuilder,
    pub clip_scroll_tree: ClipScrollTree,
}

// Message from render backend to scene builder.
pub enum SceneBuilderRequest {
    Transaction(Box<Transaction>),
    LoadScenes(Vec<LoadScene>),
    DeleteDocument(DocumentId),
    WakeUp,
    Flush(MsgSender<()>),
    SetFrameBuilderConfig(FrameBuilderConfig),
    Stop
}

// Message from scene builder to render backend.
pub enum SceneBuilderResult {
    Transaction(Box<BuiltTransaction>, Option<Sender<SceneSwapResult>>),
    FlushComplete(MsgSender<()>),
    Stopped,
}

// Message from render backend to scene builder to indicate the
// scene swap was completed. We need a separate channel for this
// so that they don't get mixed with SceneBuilderRequest messages.
pub enum SceneSwapResult {
    Complete(Sender<()>),
    Aborted,
}

pub struct SceneBuilder {
    documents: FastHashMap<DocumentId, Scene>,
    rx: Receiver<SceneBuilderRequest>,
    tx: Sender<SceneBuilderResult>,
    api_tx: MsgSender<ApiMsg>,
    config: FrameBuilderConfig,
    hooks: Option<Box<SceneBuilderHooks + Send>>,
}

impl SceneBuilder {
    pub fn new(
        config: FrameBuilderConfig,
        api_tx: MsgSender<ApiMsg>,
        hooks: Option<Box<SceneBuilderHooks + Send>>,
    ) -> (Self, Sender<SceneBuilderRequest>, Receiver<SceneBuilderResult>) {
        let (in_tx, in_rx) = channel();
        let (out_tx, out_rx) = channel();
        (
            SceneBuilder {
                documents: FastHashMap::default(),
                rx: in_rx,
                tx: out_tx,
                api_tx,
                config,
                hooks,
            },
            in_tx,
            out_rx,
        )
    }

    /// The scene builder thread's event loop.
    pub fn run(&mut self) {
        if let Some(ref hooks) = self.hooks {
            hooks.register();
        }

        loop {
            match self.rx.recv() {
                Ok(SceneBuilderRequest::WakeUp) => {}
                Ok(SceneBuilderRequest::Flush(tx)) => {
                    self.tx.send(SceneBuilderResult::FlushComplete(tx)).unwrap();
                    let _ = self.api_tx.send(ApiMsg::WakeUp);
                }
                Ok(SceneBuilderRequest::Transaction(mut txn)) => {
                    let built_txn = self.process_transaction(&mut txn);
                    self.forward_built_transaction(built_txn);
                }
                Ok(SceneBuilderRequest::DeleteDocument(document_id)) => {
                    self.documents.remove(&document_id);
                }
                Ok(SceneBuilderRequest::LoadScenes(msg)) => {
                    self.load_scenes(msg);
                }
                Ok(SceneBuilderRequest::SetFrameBuilderConfig(cfg)) => {
                    self.config = cfg;
                }
                Ok(SceneBuilderRequest::Stop) => {
                    self.tx.send(SceneBuilderResult::Stopped).unwrap();
                    // We don't need to send a WakeUp to api_tx because we only
                    // get the Stop when the RenderBackend loop is exiting.
                    break;
                }
                Err(_) => {
                    break;
                }
            }

            if let Some(ref hooks) = self.hooks {
                hooks.poke();
            }
        }

        if let Some(ref hooks) = self.hooks {
            hooks.deregister();
        }
    }

    // Specific to the replay functionality.
    fn load_scenes(&mut self, scenes: Vec<LoadScene>) {
        for item in scenes {
            self.config = item.config;

            let scene_build_start_time = precise_time_ns();

            let mut built_scene = None;
            if item.scene.has_root_pipeline() {
                let mut clip_scroll_tree = ClipScrollTree::new();
                let mut new_scene = Scene::new();

                let frame_builder = DisplayListFlattener::create_frame_builder(
                    FrameBuilder::empty(),
                    &item.scene,
                    &mut clip_scroll_tree,
                    item.font_instances,
                    &item.view,
                    &item.output_pipelines,
                    &self.config,
                    &mut new_scene,
                    item.scene_id,
                );

                built_scene = Some(BuiltScene {
                    scene: new_scene,
                    frame_builder,
                    clip_scroll_tree,
                });
            }

            self.documents.insert(item.document_id, item.scene);

            let txn = Box::new(BuiltTransaction {
                document_id: item.document_id,
                render: item.generate_frame,
                built_scene,
                resource_updates: Vec::new(),
                rasterized_blobs: Vec::new(),
                blob_rasterizer: None,
                frame_ops: Vec::new(),
                removed_pipelines: Vec::new(),
                scene_build_start_time,
                scene_build_end_time: precise_time_ns(),
            });

            self.forward_built_transaction(txn);
        }
    }

    /// Do the bulk of the work of the scene builder thread.
    fn process_transaction(&mut self, txn: &mut Transaction) -> Box<BuiltTransaction> {
        let scene_build_start_time = precise_time_ns();

        let scene = self.documents.entry(txn.document_id).or_insert(Scene::new());

        for update in txn.display_list_updates.drain(..) {
            scene.set_display_list(
                update.pipeline_id,
                update.epoch,
                update.built_display_list,
                update.background,
                update.viewport_size,
                update.content_size,
            );
        }

        for &(pipeline_id, epoch) in &txn.epoch_updates {
            scene.update_epoch(pipeline_id, epoch);
        }

        if let Some(id) = txn.set_root_pipeline {
            scene.set_root_pipeline_id(id);
        }

        for pipeline_id in &txn.removed_pipelines {
            scene.remove_pipeline(*pipeline_id)
        }

        let mut built_scene = None;
        if scene.has_root_pipeline() {
            if let Some(request) = txn.request_scene_build.take() {
                let mut clip_scroll_tree = ClipScrollTree::new();
                let mut new_scene = Scene::new();

                let frame_builder = DisplayListFlattener::create_frame_builder(
                    FrameBuilder::empty(),
                    &scene,
                    &mut clip_scroll_tree,
                    request.font_instances,
                    &request.view,
                    &request.output_pipelines,
                    &self.config,
                    &mut new_scene,
                    request.scene_id,
                );

                built_scene = Some(BuiltScene {
                    scene: new_scene,
                    frame_builder,
                    clip_scroll_tree,
                });
            }
        }

        let blob_requests = replace(&mut txn.blob_requests, Vec::new());
        let rasterized_blobs = txn.blob_rasterizer.as_mut().map_or(
            Vec::new(),
            |rasterizer| rasterizer.rasterize(&blob_requests),
        );

        // After applying the new scene we need to
        // rebuild the hit-tester, so we trigger a render
        // step.

        Box::new(BuiltTransaction {
            document_id: txn.document_id,
            render: txn.render || built_scene.is_some(),
            built_scene,
            resource_updates: replace(&mut txn.resource_updates, Vec::new()),
            rasterized_blobs: rasterized_blobs,
            blob_rasterizer: replace(&mut txn.blob_rasterizer, None),
            frame_ops: replace(&mut txn.frame_ops, Vec::new()),
            removed_pipelines: replace(&mut txn.removed_pipelines, Vec::new()),
            scene_build_start_time,
            scene_build_end_time: precise_time_ns(),
        })
    }

    /// Send the result of process_transaction back to the render backend.
    fn forward_built_transaction(&mut self, txn: Box<BuiltTransaction>) {
        // We only need the pipeline info and the result channel if we
        // have a hook callback *and* if this transaction actually built
        // a new scene that is going to get swapped in. In other cases
        // pipeline_info can be None and we can avoid some overhead from
        // invoking the hooks and blocking on the channel.
        let (pipeline_info, result_tx, result_rx) = match (&self.hooks, &txn.built_scene) {
            (&Some(ref hooks), &Some(ref built)) => {
                let info = PipelineInfo {
                    epochs: built.scene.pipeline_epochs.clone(),
                    removed_pipelines: txn.removed_pipelines.clone(),
                };
                let (tx, rx) = channel();

                hooks.pre_scene_swap(txn.scene_build_end_time - txn.scene_build_start_time);

                (Some(info), Some(tx), Some(rx))
            }
            _ => (None, None, None),
        };

        let scene_swap_start_time = precise_time_ns();
        let has_resources_updates = !txn.resource_updates.is_empty();

        self.tx.send(SceneBuilderResult::Transaction(txn, result_tx)).unwrap();

        let _ = self.api_tx.send(ApiMsg::WakeUp);

        if let Some(pipeline_info) = pipeline_info {
            // Block until the swap is done, then invoke the hook.
            let swap_result = result_rx.unwrap().recv();
            let scene_swap_time = precise_time_ns() - scene_swap_start_time;
            self.hooks.as_ref().unwrap().post_scene_swap(pipeline_info, scene_swap_time);
            // Once the hook is done, allow the RB thread to resume
            match swap_result {
                Ok(SceneSwapResult::Complete(resume_tx)) => {
                    resume_tx.send(()).ok();
                },
                _ => (),
            };
        } else if has_resources_updates {
            if let &Some(ref hooks) = &self.hooks {
                hooks.post_resource_update();
            }
        }
    }
}
<commit_msg>Move scene loading code behind the "replay" feature flag.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use api::{AsyncBlobImageRasterizer, BlobImageRequest, BlobImageParams, BlobImageResult};
use api::{DocumentId, PipelineId, ApiMsg, FrameMsg, ResourceUpdate, Epoch};
use api::{BuiltDisplayList, ColorF, LayoutSize};
use api::channel::MsgSender;
use frame_builder::{FrameBuilderConfig, FrameBuilder};
use clip_scroll_tree::ClipScrollTree;
use display_list_flattener::DisplayListFlattener;
use internal_types::{FastHashMap, FastHashSet};
use resource_cache::FontInstanceMap;
use render_backend::DocumentView;
use renderer::{PipelineInfo, SceneBuilderHooks};
use scene::Scene;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::mem::replace;
use time::precise_time_ns;

/// Represents the work associated to a transaction before scene building.
pub struct Transaction {
    pub document_id: DocumentId,
    pub display_list_updates: Vec<DisplayListUpdate>,
    pub removed_pipelines: Vec<PipelineId>,
    pub epoch_updates: Vec<(PipelineId, Epoch)>,
    pub request_scene_build: Option<SceneRequest>,
    pub blob_requests: Vec<BlobImageParams>,
    pub blob_rasterizer: Option<Box<AsyncBlobImageRasterizer>>,
    pub resource_updates: Vec<ResourceUpdate>,
    pub frame_ops: Vec<FrameMsg>,
    pub set_root_pipeline: Option<PipelineId>,
    pub render: bool,
}

impl Transaction {
    pub fn can_skip_scene_builder(&self) -> bool {
        self.request_scene_build.is_none() &&
            self.display_list_updates.is_empty() &&
            self.epoch_updates.is_empty() &&
            self.removed_pipelines.is_empty() &&
            self.blob_requests.is_empty() &&
            self.set_root_pipeline.is_none()
    }

    pub fn should_build_scene(&self) -> bool {
        !self.display_list_updates.is_empty() ||
            self.set_root_pipeline.is_some()
    }
}

/// Represent the remaining work associated to a transaction after the scene building
/// phase as well as the result of scene building itself if applicable.
pub struct BuiltTransaction {
    pub document_id: DocumentId,
    pub built_scene: Option<BuiltScene>,
    pub resource_updates: Vec<ResourceUpdate>,
    pub rasterized_blobs: Vec<(BlobImageRequest, BlobImageResult)>,
    pub blob_rasterizer: Option<Box<AsyncBlobImageRasterizer>>,
    pub frame_ops: Vec<FrameMsg>,
    pub removed_pipelines: Vec<PipelineId>,
    pub scene_build_start_time: u64,
    pub scene_build_end_time: u64,
    pub render: bool,
}

pub struct DisplayListUpdate {
    pub pipeline_id: PipelineId,
    pub epoch: Epoch,
    pub built_display_list: BuiltDisplayList,
    pub background: Option<ColorF>,
    pub viewport_size: LayoutSize,
    pub content_size: LayoutSize,
}

/// Contains the render backend data needed to build a scene.
pub struct SceneRequest {
    pub view: DocumentView,
    pub font_instances: FontInstanceMap,
    pub output_pipelines: FastHashSet<PipelineId>,
    pub scene_id: u64,
}

#[cfg(feature = "replay")]
pub struct LoadScene {
    pub document_id: DocumentId,
    pub scene: Scene,
    pub scene_id: u64,
    pub output_pipelines: FastHashSet<PipelineId>,
    pub font_instances: FontInstanceMap,
    pub view: DocumentView,
    pub config: FrameBuilderConfig,
    pub generate_frame: bool,
}

pub struct BuiltScene {
    pub scene: Scene,
    pub frame_builder: FrameBuilder,
    pub clip_scroll_tree: ClipScrollTree,
}

// Message from render backend to scene builder.
pub enum SceneBuilderRequest {
    Transaction(Box<Transaction>),
    DeleteDocument(DocumentId),
    WakeUp,
    Flush(MsgSender<()>),
    SetFrameBuilderConfig(FrameBuilderConfig),
    Stop,
    #[cfg(feature = "replay")]
    LoadScenes(Vec<LoadScene>),
}

// Message from scene builder to render backend.
pub enum SceneBuilderResult {
    Transaction(Box<BuiltTransaction>, Option<Sender<SceneSwapResult>>),
    FlushComplete(MsgSender<()>),
    Stopped,
}

// Message from render backend to scene builder to indicate the
// scene swap was completed. We need a separate channel for this
// so that they don't get mixed with SceneBuilderRequest messages.
pub enum SceneSwapResult {
    Complete(Sender<()>),
    Aborted,
}

pub struct SceneBuilder {
    documents: FastHashMap<DocumentId, Scene>,
    rx: Receiver<SceneBuilderRequest>,
    tx: Sender<SceneBuilderResult>,
    api_tx: MsgSender<ApiMsg>,
    config: FrameBuilderConfig,
    hooks: Option<Box<SceneBuilderHooks + Send>>,
}

impl SceneBuilder {
    pub fn new(
        config: FrameBuilderConfig,
        api_tx: MsgSender<ApiMsg>,
        hooks: Option<Box<SceneBuilderHooks + Send>>,
    ) -> (Self, Sender<SceneBuilderRequest>, Receiver<SceneBuilderResult>) {
        let (in_tx, in_rx) = channel();
        let (out_tx, out_rx) = channel();
        (
            SceneBuilder {
                documents: FastHashMap::default(),
                rx: in_rx,
                tx: out_tx,
                api_tx,
                config,
                hooks,
            },
            in_tx,
            out_rx,
        )
    }

    /// The scene builder thread's event loop.
    pub fn run(&mut self) {
        if let Some(ref hooks) = self.hooks {
            hooks.register();
        }

        loop {
            match self.rx.recv() {
                Ok(SceneBuilderRequest::WakeUp) => {}
                Ok(SceneBuilderRequest::Flush(tx)) => {
                    self.tx.send(SceneBuilderResult::FlushComplete(tx)).unwrap();
                    let _ = self.api_tx.send(ApiMsg::WakeUp);
                }
                Ok(SceneBuilderRequest::Transaction(mut txn)) => {
                    let built_txn = self.process_transaction(&mut txn);
                    self.forward_built_transaction(built_txn);
                }
                Ok(SceneBuilderRequest::DeleteDocument(document_id)) => {
                    self.documents.remove(&document_id);
                }
                Ok(SceneBuilderRequest::SetFrameBuilderConfig(cfg)) => {
                    self.config = cfg;
                }
                #[cfg(feature = "replay")]
                Ok(SceneBuilderRequest::LoadScenes(msg)) => {
                    self.load_scenes(msg);
                }
                Ok(SceneBuilderRequest::Stop) => {
                    self.tx.send(SceneBuilderResult::Stopped).unwrap();
                    // We don't need to send a WakeUp to api_tx because we only
                    // get the Stop when the RenderBackend loop is exiting.
                    break;
                }
                Err(_) => {
                    break;
                }
            }

            if let Some(ref hooks) = self.hooks {
                hooks.poke();
            }
        }

        if let Some(ref hooks) = self.hooks {
            hooks.deregister();
        }
    }

    #[cfg(feature = "replay")]
    fn load_scenes(&mut self, scenes: Vec<LoadScene>) {
        for item in scenes {
            self.config = item.config;

            let scene_build_start_time = precise_time_ns();

            let mut built_scene = None;
            if item.scene.has_root_pipeline() {
                let mut clip_scroll_tree = ClipScrollTree::new();
                let mut new_scene = Scene::new();

                let frame_builder = DisplayListFlattener::create_frame_builder(
                    FrameBuilder::empty(),
                    &item.scene,
                    &mut clip_scroll_tree,
                    item.font_instances,
                    &item.view,
                    &item.output_pipelines,
                    &self.config,
                    &mut new_scene,
                    item.scene_id,
                );

                built_scene = Some(BuiltScene {
                    scene: new_scene,
                    frame_builder,
                    clip_scroll_tree,
                });
            }

            self.documents.insert(item.document_id, item.scene);

            let txn = Box::new(BuiltTransaction {
                document_id: item.document_id,
                render: item.generate_frame,
                built_scene,
                resource_updates: Vec::new(),
                rasterized_blobs: Vec::new(),
                blob_rasterizer: None,
                frame_ops: Vec::new(),
                removed_pipelines: Vec::new(),
                scene_build_start_time,
                scene_build_end_time: precise_time_ns(),
            });

            self.forward_built_transaction(txn);
        }
    }

    /// Do the bulk of the work of the scene builder thread.
    fn process_transaction(&mut self, txn: &mut Transaction) -> Box<BuiltTransaction> {
        let scene_build_start_time = precise_time_ns();

        let scene = self.documents.entry(txn.document_id).or_insert(Scene::new());

        for update in txn.display_list_updates.drain(..) {
            scene.set_display_list(
                update.pipeline_id,
                update.epoch,
                update.built_display_list,
                update.background,
                update.viewport_size,
                update.content_size,
            );
        }

        for &(pipeline_id, epoch) in &txn.epoch_updates {
            scene.update_epoch(pipeline_id, epoch);
        }

        if let Some(id) = txn.set_root_pipeline {
            scene.set_root_pipeline_id(id);
        }

        for pipeline_id in &txn.removed_pipelines {
            scene.remove_pipeline(*pipeline_id)
        }

        let mut built_scene = None;
        if scene.has_root_pipeline() {
            if let Some(request) = txn.request_scene_build.take() {
                let mut clip_scroll_tree = ClipScrollTree::new();
                let mut new_scene = Scene::new();

                let frame_builder = DisplayListFlattener::create_frame_builder(
                    FrameBuilder::empty(),
                    &scene,
                    &mut clip_scroll_tree,
                    request.font_instances,
                    &request.view,
                    &request.output_pipelines,
                    &self.config,
                    &mut new_scene,
                    request.scene_id,
                );

                built_scene = Some(BuiltScene {
                    scene: new_scene,
                    frame_builder,
                    clip_scroll_tree,
                });
            }
        }

        let blob_requests = replace(&mut txn.blob_requests, Vec::new());
        let rasterized_blobs = txn.blob_rasterizer.as_mut().map_or(
            Vec::new(),
            |rasterizer| rasterizer.rasterize(&blob_requests),
        );

        // After applying the new scene we need to
        // rebuild the hit-tester, so we trigger a render
        // step.

        Box::new(BuiltTransaction {
            document_id: txn.document_id,
            render: txn.render || built_scene.is_some(),
            built_scene,
            resource_updates: replace(&mut txn.resource_updates, Vec::new()),
            rasterized_blobs: rasterized_blobs,
            blob_rasterizer: replace(&mut txn.blob_rasterizer, None),
            frame_ops: replace(&mut txn.frame_ops, Vec::new()),
            removed_pipelines: replace(&mut txn.removed_pipelines, Vec::new()),
            scene_build_start_time,
            scene_build_end_time: precise_time_ns(),
        })
    }

    /// Send the result of process_transaction back to the render backend.
    fn forward_built_transaction(&mut self, txn: Box<BuiltTransaction>) {
        // We only need the pipeline info and the result channel if we
        // have a hook callback *and* if this transaction actually built
        // a new scene that is going to get swapped in. In other cases
        // pipeline_info can be None and we can avoid some overhead from
        // invoking the hooks and blocking on the channel.
        let (pipeline_info, result_tx, result_rx) = match (&self.hooks, &txn.built_scene) {
            (&Some(ref hooks), &Some(ref built)) => {
                let info = PipelineInfo {
                    epochs: built.scene.pipeline_epochs.clone(),
                    removed_pipelines: txn.removed_pipelines.clone(),
                };
                let (tx, rx) = channel();

                hooks.pre_scene_swap(txn.scene_build_end_time - txn.scene_build_start_time);

                (Some(info), Some(tx), Some(rx))
            }
            _ => (None, None, None),
        };

        let scene_swap_start_time = precise_time_ns();
        let has_resources_updates = !txn.resource_updates.is_empty();

        self.tx.send(SceneBuilderResult::Transaction(txn, result_tx)).unwrap();

        let _ = self.api_tx.send(ApiMsg::WakeUp);

        if let Some(pipeline_info) = pipeline_info {
            // Block until the swap is done, then invoke the hook.
            let swap_result = result_rx.unwrap().recv();
            let scene_swap_time = precise_time_ns() - scene_swap_start_time;
            self.hooks.as_ref().unwrap().post_scene_swap(pipeline_info, scene_swap_time);
            // Once the hook is done, allow the RB thread to resume
            match swap_result {
                Ok(SceneSwapResult::Complete(resume_tx)) => {
                    resume_tx.send(()).ok();
                },
                _ => (),
            };
        } else if has_resources_updates {
            if let &Some(ref hooks) = &self.hooks {
                hooks.post_resource_update();
            }
        }
    }
}
<|endoftext|>


<commit_before>use core::SegmentId;
use std::collections::HashMap;
use core::SegmentMeta;
use std::fmt;
use std::fmt::{Debug, Formatter};
use indexer::segment_entry::SegmentEntry;
use indexer::delete_queue::DeleteCursor;

/// The segment register keeps track
/// of the list of segment, their size as well
/// as the state they are in.
///
/// It is consumed by indexes to get the list of
/// segments that are currently searchable,
/// and by the index merger to identify
/// merge candidates.
#[derive(Default)]
pub struct SegmentRegister {
    segment_states: HashMap<SegmentId, SegmentEntry>,
}

impl Debug for SegmentRegister {
    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
        try!(write!(f, "SegmentRegister("));
        for (k, v) in &self.segment_states {
            try!(write!(f, "{}:{}, ", k.short_uuid_string(), v.state().letter_code()));
        }
        try!(write!(f, ")"));
        Ok(())
    }
}

impl SegmentRegister {
    pub fn clear(&mut self) {
        self.segment_states.clear();
    }

    pub fn len(&self) -> usize {
        self.segment_states.len()
    }

    pub fn get_all_segments(&self) -> Vec<SegmentMeta> {
        self.segment_states
            .values()
            .map(|segment_entry| segment_entry.meta().clone())
            .collect()
    }

    pub fn get_mergeable_segments(&self) -> Vec<SegmentMeta> {
        self.segment_states
            .values()
            .filter(|segment_entry| segment_entry.is_ready())
            .map(|segment_entry| segment_entry.meta().clone())
            .collect()
    }

    pub fn segment_entries(&self) -> Vec<SegmentEntry> {
        self.segment_states.values().cloned().collect()
    }

    pub fn segment_metas(&self) -> Vec<SegmentMeta> {
        let mut segment_ids: Vec<SegmentMeta> = self.segment_states
            .values()
            .map(|segment_entry| segment_entry.meta().clone())
            .collect();
        segment_ids.sort_by_key(|meta| meta.id());
        segment_ids
    }

    pub fn segment_entry(&self, segment_id: &SegmentId) -> Option<SegmentEntry> {
        self.segment_states
            .get(&segment_id)
            .map(|segment_entry| segment_entry.clone())
    }

    pub fn contains_all(&mut self, segment_ids: &[SegmentId]) -> bool {
        segment_ids
            .iter()
            .all(|segment_id| self.segment_states.contains_key(segment_id))
    }

    pub fn add_segment_entry(&mut self, segment_entry: SegmentEntry) {
        let segment_id = segment_entry.segment_id();
        self.segment_states.insert(segment_id, segment_entry);
    }

    pub fn remove_segment(&mut self, segment_id: &SegmentId) {
        self.segment_states.remove(segment_id);
    }

    pub fn cancel_merge(&mut self, segment_id: &SegmentId) {
        self.segment_states
            .get_mut(segment_id)
            .expect("Received a merge notification for a segment that is not registered")
            .cancel_merge();
    }

    pub fn start_merge(&mut self, segment_id: &SegmentId) {
        self.segment_states
            .get_mut(segment_id)
            .expect("Received a merge notification for a segment that is not registered")
            .start_merge();
    }

    pub fn new(segment_metas: Vec<SegmentMeta>, delete_cursor: DeleteCursor) -> SegmentRegister {
        let mut segment_states = HashMap::new();
        for segment_meta in segment_metas {
            let segment_id = segment_meta.id();
            let segment_entry = SegmentEntry::new(segment_meta, delete_cursor.clone(), None);
            segment_states.insert(segment_id, segment_entry);
        }
        SegmentRegister { segment_states: segment_states }
    }
}

#[cfg(test)]
mod tests {
    use indexer::SegmentState;
    use core::SegmentId;
    use core::SegmentMeta;
    use indexer::delete_queue::*;
    use super::*;

    fn segment_ids(segment_register: &SegmentRegister) -> Vec<SegmentId> {
        segment_register
            .segment_metas()
            .into_iter()
            .map(|segment_meta| segment_meta.id())
            .collect()
    }

    #[test]
    fn test_segment_register() {
        let delete_queue = DeleteQueue::new();

        let mut segment_register = SegmentRegister::default();
        let segment_id_a = SegmentId::generate_random();
        let segment_id_b = SegmentId::generate_random();
        let segment_id_merged = SegmentId::generate_random();

        {
            let segment_meta = SegmentMeta::new(segment_id_a);
            let segment_entry = SegmentEntry::new(segment_meta, delete_queue.cursor(), None);
            segment_register.add_segment_entry(segment_entry);
        }
        assert_eq!(segment_register
                       .segment_entry(&segment_id_a)
                       .unwrap()
                       .state(),
                   SegmentState::Ready);
        assert_eq!(segment_ids(&segment_register), vec![segment_id_a]);
        {
            let segment_meta = SegmentMeta::new(segment_id_b);
            let segment_entry = SegmentEntry::new(segment_meta, delete_queue.cursor(), None);
            segment_register.add_segment_entry(segment_entry);
        }
        assert_eq!(segment_register
                       .segment_entry(&segment_id_b)
                       .unwrap()
                       .state(),
                   SegmentState::Ready);
        segment_register.start_merge(&segment_id_a);
        segment_register.start_merge(&segment_id_b);
        assert_eq!(segment_register
                       .segment_entry(&segment_id_a)
                       .unwrap()
                       .state(),
                   SegmentState::InMerge);
        assert_eq!(segment_register
                       .segment_entry(&segment_id_b)
                       .unwrap()
                       .state(),
                   SegmentState::InMerge);
        segment_register.remove_segment(&segment_id_a);
        segment_register.remove_segment(&segment_id_b);
        {
            let segment_meta_merged = SegmentMeta::new(segment_id_merged);
            let segment_entry = SegmentEntry::new(segment_meta_merged, delete_queue.cursor(), None);
            segment_register.add_segment_entry(segment_entry);
        }
        assert_eq!(segment_ids(&segment_register), vec![segment_id_merged]);
    }

}
<commit_msg>clippy: fix map_clone warnings<commit_after>use core::SegmentId;
use std::collections::HashMap;
use core::SegmentMeta;
use std::fmt;
use std::fmt::{Debug, Formatter};
use indexer::segment_entry::SegmentEntry;
use indexer::delete_queue::DeleteCursor;

/// The segment register keeps track
/// of the list of segment, their size as well
/// as the state they are in.
///
/// It is consumed by indexes to get the list of
/// segments that are currently searchable,
/// and by the index merger to identify
/// merge candidates.
#[derive(Default)]
pub struct SegmentRegister {
    segment_states: HashMap<SegmentId, SegmentEntry>,
}

impl Debug for SegmentRegister {
    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
        try!(write!(f, "SegmentRegister("));
        for (k, v) in &self.segment_states {
            try!(write!(f, "{}:{}, ", k.short_uuid_string(), v.state().letter_code()));
        }
        try!(write!(f, ")"));
        Ok(())
    }
}

impl SegmentRegister {
    pub fn clear(&mut self) {
        self.segment_states.clear();
    }

    pub fn len(&self) -> usize {
        self.segment_states.len()
    }

    pub fn get_all_segments(&self) -> Vec<SegmentMeta> {
        self.segment_states
            .values()
            .map(|segment_entry| segment_entry.meta().clone())
            .collect()
    }

    pub fn get_mergeable_segments(&self) -> Vec<SegmentMeta> {
        self.segment_states
            .values()
            .filter(|segment_entry| segment_entry.is_ready())
            .map(|segment_entry| segment_entry.meta().clone())
            .collect()
    }

    pub fn segment_entries(&self) -> Vec<SegmentEntry> {
        self.segment_states.values().cloned().collect()
    }

    pub fn segment_metas(&self) -> Vec<SegmentMeta> {
        let mut segment_ids: Vec<SegmentMeta> = self.segment_states
            .values()
            .map(|segment_entry| segment_entry.meta().clone())
            .collect();
        segment_ids.sort_by_key(|meta| meta.id());
        segment_ids
    }

    pub fn segment_entry(&self, segment_id: &SegmentId) -> Option<SegmentEntry> {
        self.segment_states.get(&segment_id).cloned()
    }

    pub fn contains_all(&mut self, segment_ids: &[SegmentId]) -> bool {
        segment_ids
            .iter()
            .all(|segment_id| self.segment_states.contains_key(segment_id))
    }

    pub fn add_segment_entry(&mut self, segment_entry: SegmentEntry) {
        let segment_id = segment_entry.segment_id();
        self.segment_states.insert(segment_id, segment_entry);
    }

    pub fn remove_segment(&mut self, segment_id: &SegmentId) {
        self.segment_states.remove(segment_id);
    }

    pub fn cancel_merge(&mut self, segment_id: &SegmentId) {
        self.segment_states
            .get_mut(segment_id)
            .expect("Received a merge notification for a segment that is not registered")
            .cancel_merge();
    }

    pub fn start_merge(&mut self, segment_id: &SegmentId) {
        self.segment_states
            .get_mut(segment_id)
            .expect("Received a merge notification for a segment that is not registered")
            .start_merge();
    }

    pub fn new(segment_metas: Vec<SegmentMeta>, delete_cursor: DeleteCursor) -> SegmentRegister {
        let mut segment_states = HashMap::new();
        for segment_meta in segment_metas {
            let segment_id = segment_meta.id();
            let segment_entry = SegmentEntry::new(segment_meta, delete_cursor.clone(), None);
            segment_states.insert(segment_id, segment_entry);
        }
        SegmentRegister { segment_states: segment_states }
    }
}

#[cfg(test)]
mod tests {
    use indexer::SegmentState;
    use core::SegmentId;
    use core::SegmentMeta;
    use indexer::delete_queue::*;
    use super::*;

    fn segment_ids(segment_register: &SegmentRegister) -> Vec<SegmentId> {
        segment_register
            .segment_metas()
            .into_iter()
            .map(|segment_meta| segment_meta.id())
            .collect()
    }

    #[test]
    fn test_segment_register() {
        let delete_queue = DeleteQueue::new();

        let mut segment_register = SegmentRegister::default();
        let segment_id_a = SegmentId::generate_random();
        let segment_id_b = SegmentId::generate_random();
        let segment_id_merged = SegmentId::generate_random();

        {
            let segment_meta = SegmentMeta::new(segment_id_a);
            let segment_entry = SegmentEntry::new(segment_meta, delete_queue.cursor(), None);
            segment_register.add_segment_entry(segment_entry);
        }
        assert_eq!(segment_register
                       .segment_entry(&segment_id_a)
                       .unwrap()
                       .state(),
                   SegmentState::Ready);
        assert_eq!(segment_ids(&segment_register), vec![segment_id_a]);
        {
            let segment_meta = SegmentMeta::new(segment_id_b);
            let segment_entry = SegmentEntry::new(segment_meta, delete_queue.cursor(), None);
            segment_register.add_segment_entry(segment_entry);
        }
        assert_eq!(segment_register
                       .segment_entry(&segment_id_b)
                       .unwrap()
                       .state(),
                   SegmentState::Ready);
        segment_register.start_merge(&segment_id_a);
        segment_register.start_merge(&segment_id_b);
        assert_eq!(segment_register
                       .segment_entry(&segment_id_a)
                       .unwrap()
                       .state(),
                   SegmentState::InMerge);
        assert_eq!(segment_register
                       .segment_entry(&segment_id_b)
                       .unwrap()
                       .state(),
                   SegmentState::InMerge);
        segment_register.remove_segment(&segment_id_a);
        segment_register.remove_segment(&segment_id_b);
        {
            let segment_meta_merged = SegmentMeta::new(segment_id_merged);
            let segment_entry = SegmentEntry::new(segment_meta_merged, delete_queue.cursor(), None);
            segment_register.add_segment_entry(segment_entry);
        }
        assert_eq!(segment_ids(&segment_register), vec![segment_id_merged]);
    }

}
<|endoftext|>


<commit_before>//! Determining which types for which we can emit `#[derive(Debug)]`.

use super::{ConstrainResult, MonotoneFramework};
use std::collections::HashSet;
use std::collections::HashMap;
use ir::context::{BindgenContext, ItemId};
use ir::item::IsOpaque;
use ir::traversal::EdgeKind;
use ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT;
use ir::ty::TypeKind;
use ir::comp::Field;
use ir::traversal::Trace;
use ir::comp::FieldMethods;
use ir::derive::CanTriviallyDeriveDebug;
use ir::comp::CompKind;

/// An analysis that finds for each IR item whether debug cannot be derived.
///
/// We use the monotone constraint function `cannot_derive_debug`, defined as
/// follows:
///
/// * If T is Opaque and layout of the type is known, get this layout as opaque
///   type and check whether it can be derived using trivial checks.
/// * If T is Array type, debug cannot be derived if the length of the array is
///   larger than the limit or the type of data the array contains cannot derive
///   debug.
/// * If T is a type alias, a templated alias or an indirection to another type,
///   debug cannot be derived if the type T refers to cannot be derived debug.
/// * If T is a compound type, debug cannot be derived if any of its base member
///   or field cannot be derived debug.
/// * If T is a pointer, T cannot be derived debug if T is a function pointer
///   and the function signature cannot be derived debug.
/// * If T is an instantiation of an abstract template definition, T cannot be
///   derived debug if any of the template arguments or template definition
///   cannot derive debug.
#[derive(Debug, Clone)]
pub struct CannotDeriveDebug<'ctx, 'gen>
    where 'gen: 'ctx
{
    ctx: &'ctx BindgenContext<'gen>,

    // The incremental result of this analysis's computation. Everything in this
    // set cannot derive debug.
    cannot_derive_debug: HashSet<ItemId>,

    // Dependencies saying that if a key ItemId has been inserted into the
    // `cannot_derive_debug` set, then each of the ids in Vec<ItemId> need to be
    // considered again.
    //
    // This is a subset of the natural IR graph with reversed edges, where we
    // only include the edges from the IR graph that can affect whether a type
    // can derive debug or not.
    dependencies: HashMap<ItemId, Vec<ItemId>>,
}

impl<'ctx, 'gen> CannotDeriveDebug<'ctx, 'gen> {
    fn consider_edge(kind: EdgeKind) -> bool {
        match kind {
            // These are the only edges that can affect whether a type can derive
            // debug or not.
            EdgeKind::BaseMember |
            EdgeKind::Field |
            EdgeKind::TypeReference |
            EdgeKind::VarType |
            EdgeKind::TemplateArgument |
            EdgeKind::TemplateDeclaration |
            EdgeKind::TemplateParameterDefinition => true,

            EdgeKind::Constructor |
            EdgeKind::Destructor |
            EdgeKind::FunctionReturn |
            EdgeKind::FunctionParameter |
            EdgeKind::InnerType |
            EdgeKind::InnerVar |
            EdgeKind::Method => false,
            EdgeKind::Generic => false,
        }
    }

    fn insert(&mut self, id: ItemId) -> ConstrainResult {
        trace!("inserting {:?} into the cannot_derive_debug set", id);

        let was_not_already_in_set = self.cannot_derive_debug.insert(id);
        assert!(
            was_not_already_in_set,
            "We shouldn't try and insert {:?} twice because if it was \
             already in the set, `constrain` should have exited early.",
            id
        );

        ConstrainResult::Changed
    }
}

impl<'ctx, 'gen> MonotoneFramework for CannotDeriveDebug<'ctx, 'gen> {
    type Node = ItemId;
    type Extra = &'ctx BindgenContext<'gen>;
    type Output = HashSet<ItemId>;

    fn new(ctx: &'ctx BindgenContext<'gen>) -> CannotDeriveDebug<'ctx, 'gen> {
        let cannot_derive_debug = HashSet::new();
        let mut dependencies = HashMap::new();

        for &item in ctx.whitelisted_items() {
            dependencies.entry(item).or_insert(vec![]);

            {
                // We reverse our natural IR graph edges to find dependencies
                // between nodes.
                item.trace(ctx, &mut |sub_item: ItemId, edge_kind| {
                    if ctx.whitelisted_items().contains(&sub_item) &&
                       Self::consider_edge(edge_kind) {
                           dependencies.entry(sub_item)
                               .or_insert(vec![])
                               .push(item);
                    }
                }, &());
            }
        }

        CannotDeriveDebug {
            ctx,
            cannot_derive_debug,
            dependencies,
        }
    }

    fn initial_worklist(&self) -> Vec<ItemId> {
        self.ctx.whitelisted_items().iter().cloned().collect()
    }

    fn constrain(&mut self, id: ItemId) -> ConstrainResult {
        trace!("constrain: {:?}", id);

        if self.cannot_derive_debug.contains(&id) {
            trace!("    already know it cannot derive Debug");
            return ConstrainResult::Same;
        }

        let item = self.ctx.resolve_item(id);
        let ty = match item.as_type() {
            Some(ty) => ty,
            None => {
                trace!("    not a type; ignoring");
                return ConstrainResult::Same;
            }
        };

        if ty.is_opaque(self.ctx, item) {
            let layout_can_derive = ty.layout(self.ctx).map_or(true, |l| {
                l.opaque().can_trivially_derive_debug(self.ctx, ())
            });
            if layout_can_derive {
                trace!("    we can trivially derive Debug for the layout");
                return ConstrainResult::Same;
            } else {
                trace!("    we cannot derive Debug for the layout");
                return self.insert(id);
            }
        }

        match *ty.kind() {
            // Handle the simple cases. These can derive debug without further
            // information.
            TypeKind::Void |
            TypeKind::NullPtr |
            TypeKind::Int(..) |
            TypeKind::Float(..) |
            TypeKind::Complex(..) |
            TypeKind::Function(..) |
            TypeKind::Enum(..) |
            TypeKind::Reference(..) |
            TypeKind::BlockPointer |
            TypeKind::Named |
            TypeKind::UnresolvedTypeRef(..) |
            TypeKind::ObjCInterface(..) |
            TypeKind::ObjCId |
            TypeKind::ObjCSel => {
                trace!("    simple type that can always derive Debug");
                ConstrainResult::Same
            }

            TypeKind::Array(t, len) => {
                if self.cannot_derive_debug.contains(&t) {
                    trace!("    arrays of T for which we cannot derive Debug \
                            also cannot derive Debug");
                    return self.insert(id);
                }

                if len <= RUST_DERIVE_IN_ARRAY_LIMIT {
                    trace!("    array is small enough to derive Debug");
                    ConstrainResult::Same
                } else {
                    trace!("    array is too large to derive Debug");
                    self.insert(id)
                }
            }

            TypeKind::ResolvedTypeRef(t) |
            TypeKind::TemplateAlias(t, _) |
            TypeKind::Alias(t) => {
                if self.cannot_derive_debug.contains(&t) {
                    trace!("    aliases and type refs to T which cannot derive \
                            Debug also cannot derive Debug");
                    self.insert(id)
                } else {
                    trace!("    aliases and type refs to T which can derive \
                            Debug can also derive Debug");
                    ConstrainResult::Same
                }
            }

            TypeKind::Comp(ref info) => {
                assert!(
                    !info.has_non_type_template_params(),
                    "The early ty.is_opaque check should have handled this case"
                );

                if info.kind() == CompKind::Union {
                    if self.ctx.options().unstable_rust {
                        trace!("    cannot derive Debug for Rust unions");
                        return self.insert(id);
                    }

                    if ty.layout(self.ctx)
                        .map_or(true,
                                |l| l.opaque().can_trivially_derive_debug(self.ctx, ())) {
                        trace!("    union layout can trivially derive Debug");
                        return ConstrainResult::Same;
                    } else {
                        trace!("    union layout cannot derive Debug");
                        return self.insert(id);
                    }
                }

                let bases_cannot_derive = info.base_members()
                    .iter()
                    .any(|base| self.cannot_derive_debug.contains(&base.ty));
                if bases_cannot_derive {
                    trace!("    base members cannot derive Debug, so we can't \
                            either");
                    return self.insert(id);
                }

                let fields_cannot_derive = info.fields()
                    .iter()
                    .any(|f| {
                        match *f {
                            Field::DataMember(ref data) => {
                                self.cannot_derive_debug.contains(&data.ty())
                            }
                            Field::Bitfields(ref bfu) => {
                                bfu.bitfields()
                                    .iter().any(|b| {
                                        self.cannot_derive_debug.contains(&b.ty())
                                    })
                            }
                        }
                    });
                if fields_cannot_derive {
                    trace!("    fields cannot derive Debug, so we can't either");
                    return self.insert(id);
                }

                trace!("    comp can derive Debug");
                ConstrainResult::Same
            }

            TypeKind::Pointer(inner) => {
                let inner_type = self.ctx.resolve_type(inner).canonical_type(self.ctx);
                if let TypeKind::Function(ref sig) = *inner_type.kind() {
                    if !sig.can_trivially_derive_debug(&self.ctx, ()) {
                        trace!("    function pointer that can't trivially derive Debug");
                        return self.insert(id);
                    }
                }
                trace!("    pointers can derive Debug");
                ConstrainResult::Same
            }

            TypeKind::TemplateInstantiation(ref template) => {
                let args_cannot_derive = template.template_arguments()
                    .iter()
                    .any(|arg| self.cannot_derive_debug.contains(&arg));
                if args_cannot_derive {
                    trace!("    template args cannot derive Debug, so \
                            insantiation can't either");
                    return self.insert(id);
                }

                assert!(
                    !template.template_definition().is_opaque(self.ctx, &()),
                    "The early ty.is_opaque check should have handled this case"
                );
                let def_cannot_derive = self.cannot_derive_debug
                    .contains(&template.template_definition());
                if def_cannot_derive {
                    trace!("    template definition cannot derive Debug, so \
                            insantiation can't either");
                    return self.insert(id);
                }

                trace!("    template instantiation can derive Debug");
                ConstrainResult::Same
            }

            TypeKind::Opaque => {
                unreachable!(
                    "The early ty.is_opaque check should have handled this case"
                )
            }
        }
    }

    fn each_depending_on<F>(&self, id: ItemId, mut f: F)
        where F: FnMut(ItemId),
    {
        if let Some(edges) = self.dependencies.get(&id) {
            for item in edges {
                trace!("enqueue {:?} into worklist", item);
                f(*item);
            }
        }
    }
}

impl<'ctx, 'gen> From<CannotDeriveDebug<'ctx, 'gen>> for HashSet<ItemId> {
    fn from(analysis: CannotDeriveDebug<'ctx, 'gen>) -> Self {
        analysis.cannot_derive_debug
    }
}
<commit_msg>Pull `return` up out of the branch arms<commit_after>//! Determining which types for which we can emit `#[derive(Debug)]`.

use super::{ConstrainResult, MonotoneFramework};
use std::collections::HashSet;
use std::collections::HashMap;
use ir::context::{BindgenContext, ItemId};
use ir::item::IsOpaque;
use ir::traversal::EdgeKind;
use ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT;
use ir::ty::TypeKind;
use ir::comp::Field;
use ir::traversal::Trace;
use ir::comp::FieldMethods;
use ir::derive::CanTriviallyDeriveDebug;
use ir::comp::CompKind;

/// An analysis that finds for each IR item whether debug cannot be derived.
///
/// We use the monotone constraint function `cannot_derive_debug`, defined as
/// follows:
///
/// * If T is Opaque and layout of the type is known, get this layout as opaque
///   type and check whether it can be derived using trivial checks.
/// * If T is Array type, debug cannot be derived if the length of the array is
///   larger than the limit or the type of data the array contains cannot derive
///   debug.
/// * If T is a type alias, a templated alias or an indirection to another type,
///   debug cannot be derived if the type T refers to cannot be derived debug.
/// * If T is a compound type, debug cannot be derived if any of its base member
///   or field cannot be derived debug.
/// * If T is a pointer, T cannot be derived debug if T is a function pointer
///   and the function signature cannot be derived debug.
/// * If T is an instantiation of an abstract template definition, T cannot be
///   derived debug if any of the template arguments or template definition
///   cannot derive debug.
#[derive(Debug, Clone)]
pub struct CannotDeriveDebug<'ctx, 'gen>
    where 'gen: 'ctx
{
    ctx: &'ctx BindgenContext<'gen>,

    // The incremental result of this analysis's computation. Everything in this
    // set cannot derive debug.
    cannot_derive_debug: HashSet<ItemId>,

    // Dependencies saying that if a key ItemId has been inserted into the
    // `cannot_derive_debug` set, then each of the ids in Vec<ItemId> need to be
    // considered again.
    //
    // This is a subset of the natural IR graph with reversed edges, where we
    // only include the edges from the IR graph that can affect whether a type
    // can derive debug or not.
    dependencies: HashMap<ItemId, Vec<ItemId>>,
}

impl<'ctx, 'gen> CannotDeriveDebug<'ctx, 'gen> {
    fn consider_edge(kind: EdgeKind) -> bool {
        match kind {
            // These are the only edges that can affect whether a type can derive
            // debug or not.
            EdgeKind::BaseMember |
            EdgeKind::Field |
            EdgeKind::TypeReference |
            EdgeKind::VarType |
            EdgeKind::TemplateArgument |
            EdgeKind::TemplateDeclaration |
            EdgeKind::TemplateParameterDefinition => true,

            EdgeKind::Constructor |
            EdgeKind::Destructor |
            EdgeKind::FunctionReturn |
            EdgeKind::FunctionParameter |
            EdgeKind::InnerType |
            EdgeKind::InnerVar |
            EdgeKind::Method => false,
            EdgeKind::Generic => false,
        }
    }

    fn insert(&mut self, id: ItemId) -> ConstrainResult {
        trace!("inserting {:?} into the cannot_derive_debug set", id);

        let was_not_already_in_set = self.cannot_derive_debug.insert(id);
        assert!(
            was_not_already_in_set,
            "We shouldn't try and insert {:?} twice because if it was \
             already in the set, `constrain` should have exited early.",
            id
        );

        ConstrainResult::Changed
    }
}

impl<'ctx, 'gen> MonotoneFramework for CannotDeriveDebug<'ctx, 'gen> {
    type Node = ItemId;
    type Extra = &'ctx BindgenContext<'gen>;
    type Output = HashSet<ItemId>;

    fn new(ctx: &'ctx BindgenContext<'gen>) -> CannotDeriveDebug<'ctx, 'gen> {
        let cannot_derive_debug = HashSet::new();
        let mut dependencies = HashMap::new();

        for &item in ctx.whitelisted_items() {
            dependencies.entry(item).or_insert(vec![]);

            {
                // We reverse our natural IR graph edges to find dependencies
                // between nodes.
                item.trace(ctx, &mut |sub_item: ItemId, edge_kind| {
                    if ctx.whitelisted_items().contains(&sub_item) &&
                       Self::consider_edge(edge_kind) {
                           dependencies.entry(sub_item)
                               .or_insert(vec![])
                               .push(item);
                    }
                }, &());
            }
        }

        CannotDeriveDebug {
            ctx,
            cannot_derive_debug,
            dependencies,
        }
    }

    fn initial_worklist(&self) -> Vec<ItemId> {
        self.ctx.whitelisted_items().iter().cloned().collect()
    }

    fn constrain(&mut self, id: ItemId) -> ConstrainResult {
        trace!("constrain: {:?}", id);

        if self.cannot_derive_debug.contains(&id) {
            trace!("    already know it cannot derive Debug");
            return ConstrainResult::Same;
        }

        let item = self.ctx.resolve_item(id);
        let ty = match item.as_type() {
            Some(ty) => ty,
            None => {
                trace!("    not a type; ignoring");
                return ConstrainResult::Same;
            }
        };

        if ty.is_opaque(self.ctx, item) {
            let layout_can_derive = ty.layout(self.ctx).map_or(true, |l| {
                l.opaque().can_trivially_derive_debug(self.ctx, ())
            });
            return if layout_can_derive {
                trace!("    we can trivially derive Debug for the layout");
                ConstrainResult::Same
            } else {
                trace!("    we cannot derive Debug for the layout");
                self.insert(id)
            };
        }

        match *ty.kind() {
            // Handle the simple cases. These can derive debug without further
            // information.
            TypeKind::Void |
            TypeKind::NullPtr |
            TypeKind::Int(..) |
            TypeKind::Float(..) |
            TypeKind::Complex(..) |
            TypeKind::Function(..) |
            TypeKind::Enum(..) |
            TypeKind::Reference(..) |
            TypeKind::BlockPointer |
            TypeKind::Named |
            TypeKind::UnresolvedTypeRef(..) |
            TypeKind::ObjCInterface(..) |
            TypeKind::ObjCId |
            TypeKind::ObjCSel => {
                trace!("    simple type that can always derive Debug");
                ConstrainResult::Same
            }

            TypeKind::Array(t, len) => {
                if self.cannot_derive_debug.contains(&t) {
                    trace!("    arrays of T for which we cannot derive Debug \
                            also cannot derive Debug");
                    return self.insert(id);
                }

                if len <= RUST_DERIVE_IN_ARRAY_LIMIT {
                    trace!("    array is small enough to derive Debug");
                    ConstrainResult::Same
                } else {
                    trace!("    array is too large to derive Debug");
                    self.insert(id)
                }
            }

            TypeKind::ResolvedTypeRef(t) |
            TypeKind::TemplateAlias(t, _) |
            TypeKind::Alias(t) => {
                if self.cannot_derive_debug.contains(&t) {
                    trace!("    aliases and type refs to T which cannot derive \
                            Debug also cannot derive Debug");
                    self.insert(id)
                } else {
                    trace!("    aliases and type refs to T which can derive \
                            Debug can also derive Debug");
                    ConstrainResult::Same
                }
            }

            TypeKind::Comp(ref info) => {
                assert!(
                    !info.has_non_type_template_params(),
                    "The early ty.is_opaque check should have handled this case"
                );

                if info.kind() == CompKind::Union {
                    if self.ctx.options().unstable_rust {
                        trace!("    cannot derive Debug for Rust unions");
                        return self.insert(id);
                    }

                    if ty.layout(self.ctx)
                        .map_or(true,
                                |l| l.opaque().can_trivially_derive_debug(self.ctx, ())) {
                        trace!("    union layout can trivially derive Debug");
                        return ConstrainResult::Same;
                    } else {
                        trace!("    union layout cannot derive Debug");
                        return self.insert(id);
                    }
                }

                let bases_cannot_derive = info.base_members()
                    .iter()
                    .any(|base| self.cannot_derive_debug.contains(&base.ty));
                if bases_cannot_derive {
                    trace!("    base members cannot derive Debug, so we can't \
                            either");
                    return self.insert(id);
                }

                let fields_cannot_derive = info.fields()
                    .iter()
                    .any(|f| {
                        match *f {
                            Field::DataMember(ref data) => {
                                self.cannot_derive_debug.contains(&data.ty())
                            }
                            Field::Bitfields(ref bfu) => {
                                bfu.bitfields()
                                    .iter().any(|b| {
                                        self.cannot_derive_debug.contains(&b.ty())
                                    })
                            }
                        }
                    });
                if fields_cannot_derive {
                    trace!("    fields cannot derive Debug, so we can't either");
                    return self.insert(id);
                }

                trace!("    comp can derive Debug");
                ConstrainResult::Same
            }

            TypeKind::Pointer(inner) => {
                let inner_type = self.ctx.resolve_type(inner).canonical_type(self.ctx);
                if let TypeKind::Function(ref sig) = *inner_type.kind() {
                    if !sig.can_trivially_derive_debug(&self.ctx, ()) {
                        trace!("    function pointer that can't trivially derive Debug");
                        return self.insert(id);
                    }
                }
                trace!("    pointers can derive Debug");
                ConstrainResult::Same
            }

            TypeKind::TemplateInstantiation(ref template) => {
                let args_cannot_derive = template.template_arguments()
                    .iter()
                    .any(|arg| self.cannot_derive_debug.contains(&arg));
                if args_cannot_derive {
                    trace!("    template args cannot derive Debug, so \
                            insantiation can't either");
                    return self.insert(id);
                }

                assert!(
                    !template.template_definition().is_opaque(self.ctx, &()),
                    "The early ty.is_opaque check should have handled this case"
                );
                let def_cannot_derive = self.cannot_derive_debug
                    .contains(&template.template_definition());
                if def_cannot_derive {
                    trace!("    template definition cannot derive Debug, so \
                            insantiation can't either");
                    return self.insert(id);
                }

                trace!("    template instantiation can derive Debug");
                ConstrainResult::Same
            }

            TypeKind::Opaque => {
                unreachable!(
                    "The early ty.is_opaque check should have handled this case"
                )
            }
        }
    }

    fn each_depending_on<F>(&self, id: ItemId, mut f: F)
        where F: FnMut(ItemId),
    {
        if let Some(edges) = self.dependencies.get(&id) {
            for item in edges {
                trace!("enqueue {:?} into worklist", item);
                f(*item);
            }
        }
    }
}

impl<'ctx, 'gen> From<CannotDeriveDebug<'ctx, 'gen>> for HashSet<ItemId> {
    fn from(analysis: CannotDeriveDebug<'ctx, 'gen>) -> Self {
        analysis.cannot_derive_debug
    }
}
<|endoftext|>