<commit_before>pub mod autotag;
pub mod queue_tag;
pub mod tag_command;
pub mod update_tag;
pub mod update_tags;

pub use self::autotag::AutoTagCommand;
pub use self::queue_tag::QueueTagCommand;
pub use self::tag_command::TagCommand;
pub use self::update_tag::UpdateTagCommand;
pub use self::update_tags::UpdateTagsCommand;

use bot::BotEnv;
use database::models::{ToU64, Tag, NewTag, Verification, Role as DbRole};

use lalafell::error::*;
use lalafell::commands::prelude::*;

use serenity::Error as SError;
use serenity::builder::EditRole;
use serenity::model::guild::Role;
use serenity::prelude::Mentionable;
use serenity::model::id::{RoleId, UserId};
use serenity::http::{HttpError, StatusCode};

use diesel::prelude::*;

use url::Url;

use reqwest::Client;

use std::thread;
use std::sync::Mutex;
use std::collections::HashSet;

lazy_static! {
  // Limbo roles are roles that may or may not be added to the Discord bot state.
  static ref LIMBO_ROLES: Mutex<Vec<Role>> = Mutex::default();
}

pub struct Tagger;

impl Tagger {
  fn add_to_xivdb<N: Into<String>, S: Into<String>>(name: N, server: S) {
    let name: String = name.into();
    let server: String = server.into();
    thread::spawn(move || {
      let mut url = Url::parse("https://xivsync.com/character/search").unwrap();
      url.query_pairs_mut()
        .append_pair("name", &name)
        .append_pair("server", &server);
      let client = Client::new();
      client.get(url).send().ok();
    });
  }

  pub fn search_tag(env: &BotEnv, who: UserId, on: GuildId, server: &str, character_name: &str, ignore_verified: bool) -> Result<Option<String>> {
    let params = &[
      ("one", "characters"),
      ("strict", "on"),
      ("server|et", server)
    ];

    let res = env.xivdb.search(character_name, params).chain_err(|| "could not query XIVDB")?;

    let search_chars = res.characters.unwrap().results;
    if search_chars.is_empty() {
      Tagger::add_to_xivdb(character_name, server);
      return Ok(Some(format!("Could not find any character by the name {} on {} in the XIVDB database.\nIf you typed everything correctly, please wait five minutes for your character to get added to the database, then try again.", character_name, server)));
    }

    let char_id = match search_chars[0]["id"].as_u64() {
      Some(u) => u,
      None => bail!("character ID was not a u64")
    };

    let name = match search_chars[0]["name"].as_str() {
      Some(s) => s,
      None => bail!("character name was not a string")
    };

    if name.to_lowercase() != character_name.to_lowercase() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    Tagger::tag(env, who, on, char_id, ignore_verified)
  }

  fn find_or_create_role(guild: GuildId, name: &str, add_roles: &mut Vec<Role>, created_roles: &mut Vec<Role>) -> Result<()> {
    Tagger::find_or_create_role_and(guild, name, add_roles, created_roles, |r| r)
  }

  fn find_or_create_role_and<F>(guild: GuildId, name: &str, add_roles: &mut Vec<Role>, created_roles: &mut Vec<Role>, f: F) -> Result<()>
    where F: FnOnce(EditRole) -> EditRole
  {
    let lower_name = name.to_lowercase();
    let guild = guild.get().chain_err(|| "could not get guild")?;
    match guild.roles.values().find(|x| x.name.to_lowercase() == lower_name) {
      Some(r) => add_roles.push(r.clone()),
      None => {
        let role = guild.create_role(|r| f(r).name(&lower_name)).chain_err(|| "could not create role")?;
        created_roles.push(role);
      }
    }
    Ok(())
  }

  fn get_roles() -> Result<Vec<String>> {
    let roles: Vec<DbRole> = ::bot::CONNECTION.with(|c| {
      use database::schema::roles::dsl;
      dsl::roles.load(c).chain_err(|| "could not load roles")
    })?;
    Ok(roles.into_iter().map(|x| x.role_name.to_lowercase()).collect())
  }

  pub fn tag(env: &BotEnv, who: UserId, on: GuildId, char_id: u64, ignore_verified: bool) -> Result<Option<String>> {
    let tag: Option<Tag> = ::bot::CONNECTION.with(|c| {
      use database::schema::tags::dsl;
      dsl::tags
        .filter(dsl::user_id.eq(who.to_u64()).and(dsl::server_id.eq(on.to_u64())))
        .first(c)
        .optional()
        .chain_err(|| "could not load tags")
    })?;
    let is_verified = match tag {
      Some(t) => {
        let verification: Verification = ::bot::CONNECTION.with(|c| {
          Verification::belonging_to(&t).first(c).optional().chain_err(|| "could not load verifications")
        })?.unwrap_or_default();
        if verification.verified && !ignore_verified && char_id != *t.character_id {
          return Ok(Some(format!("{} is verified as {} on {}, so they cannot switch to another account.", who.mention(), t.character, t.server)));
        }
        verification.verified
      },
      None => false
    };

    // This is still a disaster, just slightly less so
    let member = match on.member(who) {
      Ok(m) => m,
      Err(SError::Http(HttpError::UnsuccessfulRequest(ref r))) if r.status == StatusCode::NotFound => {
        ::bot::CONNECTION.with(|c| {
          use database::schema::tags::dsl;
          ::diesel::delete(dsl::tags
            .filter(dsl::user_id.eq(who.to_u64()).and(dsl::server_id.eq(on.to_u64()))))
            .execute(c)
            .chain_err(|| format!("could not remove tag {} on {}, but it was expected to be in database", who.0, on.0))
        })?;
        bail!("could not get user {} as a member of {}: removing their tag", who.0, on.0);
      },
      Err(e) => return Err(e).chain_err(|| "could not get member for tagging")
    };

    let character = env.xivdb.character(char_id).chain_err(|| "could not look up character")?;

    let tag: Option<Tag> = ::bot::CONNECTION.with(|c| {
      use database::schema::tags::dsl;
      dsl::tags
        .filter(dsl::user_id.eq(who.to_u64()).and(dsl::server_id.eq(on.to_u64())))
        .first(c)
        .optional()
        .chain_err(|| "could not load tags")
    })?;
    match tag {
      Some(mut t) => {
        let (id, name, server) = (character.lodestone_id, character.name.clone(), character.server.clone());
        t.character_id = id.into();
        t.character = name;
        t.server = server;
        ::bot::CONNECTION.with(|c| t.save_changes::<Tag>(c).chain_err(|| "could not update tag"))?;
      },
      None => {
        let new_tag = NewTag::new(
          who.0,
          on.0,
          character.lodestone_id,
          &character.name,
          &character.server
        );
        ::bot::CONNECTION.with(|c| {
          use database::schema::tags;
          ::diesel::insert_into(tags::table).values(&new_tag).execute(c).chain_err(|| "could not insert tag")
        })?;
      }
    }

    // Get a copy of the roles on the server.
    let mut roles: Vec<Role> = match on.find() {
      Some(g) => g.read().roles.values().cloned().collect(),
      None => on.get().chain_err(|| "could not get guild")?.roles.values().cloned().collect()
    };

    // Check for existing limbo roles.
    {
      let limbo = &mut *LIMBO_ROLES.lock().unwrap();
      for role in &roles {
        // If the server has updated to contain the limbo role, remove it.
        if let Some(i) = limbo.iter().position(|x| x.id == role.id) {
          limbo.remove(i);
        }
      }
    }
    // Get a copy of the limbo roles.
    let limbo = LIMBO_ROLES.lock().unwrap().clone();
    // Extend the server roles with the limbo roles.
    roles.extend(limbo);

    // Find or create the necessary roles
    let mut created_roles = Vec::new();
    let mut add_roles = Vec::new();
    Tagger::find_or_create_role(on, &character.data.race, &mut add_roles, &mut created_roles)?;
    Tagger::find_or_create_role(on, &character.data.gender, &mut add_roles, &mut created_roles)?;
    Tagger::find_or_create_role_and(on, &character.server, &mut add_roles, &mut created_roles, |r| r.hoist(true))?;

    if is_verified {
      Tagger::find_or_create_role(on, "verified", &mut add_roles, &mut created_roles)?;
    }

    // If we created any roles, the server may or may not update with them fast enough, so store a copy in the limbo
    // roles.
    {
      let limbo = &mut *LIMBO_ROLES.lock().unwrap();
      for created in &created_roles {
        limbo.push(created.clone());
      }
    }

    debug!("Created the following roles:\n:{:#?}", created_roles);

    debug!("Existing roles:\n{:#?}", add_roles);

    // Extend the roles to add with the roles we created.
    add_roles.extend(created_roles);
    // Get all the roles that are part of groups
    let all_group_roles = Tagger::get_roles()?;
    // Filter all roles on the server to only the roles the member has
    let keep: Vec<&Role> = roles.iter().filter(|x| member.roles.contains(&x.id)).collect();
    // Filter all the roles the member has, keeping the ones not in a group. These roles will not be touched when
    // updating the tag.
    let keep: Vec<&Role> = keep.into_iter().filter(|x| !all_group_roles.contains(&x.name.to_lowercase())).collect();
    debug!("Roles to keep:\n{:#?}", keep);
    // Combine the two sets of roles and map them to IDs
    let mut role_set: Vec<RoleId> = add_roles.iter().map(|r| r.id).chain(keep.into_iter().map(|r| r.id)).collect();
    // Sort the IDs so we can dedup them
    role_set.sort();
    // Remove the duplicate roles, if any
    role_set.dedup();

    debug!("Final role set:\n{:#?}", role_set);

    // Only update the roles if they are different
    let different = {
      let member_roles: HashSet<u64> = member.roles.iter().map(|x| x.0).collect();
      let actual_role_set: HashSet<u64> = role_set.iter().map(|x| x.0).collect();
      member_roles != actual_role_set
    };
    if different {
      on.edit_member(who, |m| m.roles(&role_set)).chain_err(|| "could not add roles")?;
    }

    // cannot edit nickname of those with a higher role
    if member.nick.as_ref() != Some(&character.name) {
      on.edit_member(who, |m| m.nickname(&character.name)).ok();
    }
    Ok(None)
  }
}

#[derive(Debug, Deserialize)]
struct DiscordNotFoundError {
  code: u64,
  message: Option<String>
}
<commit_msg>fix: create new roles with no permissions by default<commit_after>pub mod autotag;
pub mod queue_tag;
pub mod tag_command;
pub mod update_tag;
pub mod update_tags;

pub use self::autotag::AutoTagCommand;
pub use self::queue_tag::QueueTagCommand;
pub use self::tag_command::TagCommand;
pub use self::update_tag::UpdateTagCommand;
pub use self::update_tags::UpdateTagsCommand;

use bot::BotEnv;
use database::models::{ToU64, Tag, NewTag, Verification, Role as DbRole};

use lalafell::error::*;
use lalafell::commands::prelude::*;

use serenity::Error as SError;
use serenity::builder::EditRole;
use serenity::model::guild::Role;
use serenity::model::permissions::Permissions;
use serenity::prelude::Mentionable;
use serenity::model::id::{RoleId, UserId};
use serenity::http::{HttpError, StatusCode};

use diesel::prelude::*;

use url::Url;

use reqwest::Client;

use std::thread;
use std::sync::Mutex;
use std::collections::HashSet;

lazy_static! {
  // Limbo roles are roles that may or may not be added to the Discord bot state.
  static ref LIMBO_ROLES: Mutex<Vec<Role>> = Mutex::default();
}

pub struct Tagger;

impl Tagger {
  fn add_to_xivdb<N: Into<String>, S: Into<String>>(name: N, server: S) {
    let name: String = name.into();
    let server: String = server.into();
    thread::spawn(move || {
      let mut url = Url::parse("https://xivsync.com/character/search").unwrap();
      url.query_pairs_mut()
        .append_pair("name", &name)
        .append_pair("server", &server);
      let client = Client::new();
      client.get(url).send().ok();
    });
  }

  pub fn search_tag(env: &BotEnv, who: UserId, on: GuildId, server: &str, character_name: &str, ignore_verified: bool) -> Result<Option<String>> {
    let params = &[
      ("one", "characters"),
      ("strict", "on"),
      ("server|et", server)
    ];

    let res = env.xivdb.search(character_name, params).chain_err(|| "could not query XIVDB")?;

    let search_chars = res.characters.unwrap().results;
    if search_chars.is_empty() {
      Tagger::add_to_xivdb(character_name, server);
      return Ok(Some(format!("Could not find any character by the name {} on {} in the XIVDB database.\nIf you typed everything correctly, please wait five minutes for your character to get added to the database, then try again.", character_name, server)));
    }

    let char_id = match search_chars[0]["id"].as_u64() {
      Some(u) => u,
      None => bail!("character ID was not a u64")
    };

    let name = match search_chars[0]["name"].as_str() {
      Some(s) => s,
      None => bail!("character name was not a string")
    };

    if name.to_lowercase() != character_name.to_lowercase() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    Tagger::tag(env, who, on, char_id, ignore_verified)
  }

  fn find_or_create_role(guild: GuildId, name: &str, add_roles: &mut Vec<Role>, created_roles: &mut Vec<Role>) -> Result<()> {
    Tagger::find_or_create_role_and(guild, name, add_roles, created_roles, |r| r)
  }

  fn find_or_create_role_and<F>(guild: GuildId, name: &str, add_roles: &mut Vec<Role>, created_roles: &mut Vec<Role>, f: F) -> Result<()>
    where F: FnOnce(EditRole) -> EditRole
  {
    let lower_name = name.to_lowercase();
    let guild = guild.get().chain_err(|| "could not get guild")?;
    match guild.roles.values().find(|x| x.name.to_lowercase() == lower_name) {
      Some(r) => add_roles.push(r.clone()),
      None => {
        let role = guild.create_role(|r| f(r.permissions(Permissions::empty())).name(&lower_name)).chain_err(|| "could not create role")?;
        created_roles.push(role);
      }
    }
    Ok(())
  }

  fn get_roles() -> Result<Vec<String>> {
    let roles: Vec<DbRole> = ::bot::CONNECTION.with(|c| {
      use database::schema::roles::dsl;
      dsl::roles.load(c).chain_err(|| "could not load roles")
    })?;
    Ok(roles.into_iter().map(|x| x.role_name.to_lowercase()).collect())
  }

  pub fn tag(env: &BotEnv, who: UserId, on: GuildId, char_id: u64, ignore_verified: bool) -> Result<Option<String>> {
    let tag: Option<Tag> = ::bot::CONNECTION.with(|c| {
      use database::schema::tags::dsl;
      dsl::tags
        .filter(dsl::user_id.eq(who.to_u64()).and(dsl::server_id.eq(on.to_u64())))
        .first(c)
        .optional()
        .chain_err(|| "could not load tags")
    })?;
    let is_verified = match tag {
      Some(t) => {
        let verification: Verification = ::bot::CONNECTION.with(|c| {
          Verification::belonging_to(&t).first(c).optional().chain_err(|| "could not load verifications")
        })?.unwrap_or_default();
        if verification.verified && !ignore_verified && char_id != *t.character_id {
          return Ok(Some(format!("{} is verified as {} on {}, so they cannot switch to another account.", who.mention(), t.character, t.server)));
        }
        verification.verified
      },
      None => false
    };

    // This is still a disaster, just slightly less so
    let member = match on.member(who) {
      Ok(m) => m,
      Err(SError::Http(HttpError::UnsuccessfulRequest(ref r))) if r.status == StatusCode::NotFound => {
        ::bot::CONNECTION.with(|c| {
          use database::schema::tags::dsl;
          ::diesel::delete(dsl::tags
            .filter(dsl::user_id.eq(who.to_u64()).and(dsl::server_id.eq(on.to_u64()))))
            .execute(c)
            .chain_err(|| format!("could not remove tag {} on {}, but it was expected to be in database", who.0, on.0))
        })?;
        bail!("could not get user {} as a member of {}: removing their tag", who.0, on.0);
      },
      Err(e) => return Err(e).chain_err(|| "could not get member for tagging")
    };

    let character = env.xivdb.character(char_id).chain_err(|| "could not look up character")?;

    let tag: Option<Tag> = ::bot::CONNECTION.with(|c| {
      use database::schema::tags::dsl;
      dsl::tags
        .filter(dsl::user_id.eq(who.to_u64()).and(dsl::server_id.eq(on.to_u64())))
        .first(c)
        .optional()
        .chain_err(|| "could not load tags")
    })?;
    match tag {
      Some(mut t) => {
        let (id, name, server) = (character.lodestone_id, character.name.clone(), character.server.clone());
        t.character_id = id.into();
        t.character = name;
        t.server = server;
        ::bot::CONNECTION.with(|c| t.save_changes::<Tag>(c).chain_err(|| "could not update tag"))?;
      },
      None => {
        let new_tag = NewTag::new(
          who.0,
          on.0,
          character.lodestone_id,
          &character.name,
          &character.server
        );
        ::bot::CONNECTION.with(|c| {
          use database::schema::tags;
          ::diesel::insert_into(tags::table).values(&new_tag).execute(c).chain_err(|| "could not insert tag")
        })?;
      }
    }

    // Get a copy of the roles on the server.
    let mut roles: Vec<Role> = match on.find() {
      Some(g) => g.read().roles.values().cloned().collect(),
      None => on.get().chain_err(|| "could not get guild")?.roles.values().cloned().collect()
    };

    // Check for existing limbo roles.
    {
      let limbo = &mut *LIMBO_ROLES.lock().unwrap();
      for role in &roles {
        // If the server has updated to contain the limbo role, remove it.
        if let Some(i) = limbo.iter().position(|x| x.id == role.id) {
          limbo.remove(i);
        }
      }
    }
    // Get a copy of the limbo roles.
    let limbo = LIMBO_ROLES.lock().unwrap().clone();
    // Extend the server roles with the limbo roles.
    roles.extend(limbo);

    // Find or create the necessary roles
    let mut created_roles = Vec::new();
    let mut add_roles = Vec::new();
    Tagger::find_or_create_role(on, &character.data.race, &mut add_roles, &mut created_roles)?;
    Tagger::find_or_create_role(on, &character.data.gender, &mut add_roles, &mut created_roles)?;
    Tagger::find_or_create_role_and(on, &character.server, &mut add_roles, &mut created_roles, |r| r.hoist(true))?;

    if is_verified {
      Tagger::find_or_create_role(on, "verified", &mut add_roles, &mut created_roles)?;
    }

    // If we created any roles, the server may or may not update with them fast enough, so store a copy in the limbo
    // roles.
    {
      let limbo = &mut *LIMBO_ROLES.lock().unwrap();
      for created in &created_roles {
        limbo.push(created.clone());
      }
    }

    debug!("Created the following roles:\n:{:#?}", created_roles);

    debug!("Existing roles:\n{:#?}", add_roles);

    // Extend the roles to add with the roles we created.
    add_roles.extend(created_roles);
    // Get all the roles that are part of groups
    let all_group_roles = Tagger::get_roles()?;
    // Filter all roles on the server to only the roles the member has
    let keep: Vec<&Role> = roles.iter().filter(|x| member.roles.contains(&x.id)).collect();
    // Filter all the roles the member has, keeping the ones not in a group. These roles will not be touched when
    // updating the tag.
    let keep: Vec<&Role> = keep.into_iter().filter(|x| !all_group_roles.contains(&x.name.to_lowercase())).collect();
    debug!("Roles to keep:\n{:#?}", keep);
    // Combine the two sets of roles and map them to IDs
    let mut role_set: Vec<RoleId> = add_roles.iter().map(|r| r.id).chain(keep.into_iter().map(|r| r.id)).collect();
    // Sort the IDs so we can dedup them
    role_set.sort();
    // Remove the duplicate roles, if any
    role_set.dedup();

    debug!("Final role set:\n{:#?}", role_set);

    // Only update the roles if they are different
    let different = {
      let member_roles: HashSet<u64> = member.roles.iter().map(|x| x.0).collect();
      let actual_role_set: HashSet<u64> = role_set.iter().map(|x| x.0).collect();
      member_roles != actual_role_set
    };
    if different {
      on.edit_member(who, |m| m.roles(&role_set)).chain_err(|| "could not add roles")?;
    }

    // cannot edit nickname of those with a higher role
    if member.nick.as_ref() != Some(&character.name) {
      on.edit_member(who, |m| m.nickname(&character.name)).ok();
    }
    Ok(None)
  }
}

#[derive(Debug, Deserialize)]
struct DiscordNotFoundError {
  code: u64,
  message: Option<String>
}
<|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::{ApiMsg, DebugCommand};
use api::channel::MsgSender;
use std::sync::mpsc::{channel, Receiver};
use std::thread;
use std::sync::mpsc::Sender;

use ws;

// Messages that are sent from the render backend to the renderer
// debug command queue. These are sent in a separate queue so
// that none of these types are exposed to the RenderApi interfaces.
// We can't use select!() as it's not stable...
pub enum DebugMsg {
    FetchPasses(ws::Sender),
}

// Represents a connection to a client.
struct Server {
    ws: ws::Sender,
    debug_tx: Sender<DebugMsg>,
    api_tx: MsgSender<ApiMsg>,
}

impl ws::Handler for Server {
    fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
        match msg {
            ws::Message::Text(string) => {
                let cmd = match string.as_str() {
                    "enable_profiler" => {
                        DebugCommand::EnableProfiler(true)
                    }
                    "disable_profiler" => {
                        DebugCommand::EnableProfiler(false)
                    }
                    "enable_texture_cache_debug" => {
                        DebugCommand::EnableTextureCacheDebug(true)
                    }
                    "disable_texture_cache_debug" => {
                        DebugCommand::EnableTextureCacheDebug(false)
                    }
                    "enable_render_target_debug" => {
                        DebugCommand::EnableRenderTargetDebug(true)
                    }
                    "disable_render_target_debug" => {
                        DebugCommand::EnableRenderTargetDebug(false)
                    }
                    "fetch_passes" => {
                        let msg = DebugMsg::FetchPasses(self.ws.clone());
                        self.debug_tx.send(msg).unwrap();
                        DebugCommand::Flush
                    }
                    msg => {
                        println!("unknown msg {}", msg);
                        return Ok(());
                    }
                };

                let msg = ApiMsg::DebugCommand(cmd);
                self.api_tx.send(msg).unwrap();
            }
            ws::Message::Binary(..) => {}
        }

        Ok(())
    }
}

// Spawn a thread for a given renderer, and wait for
// client connections.
pub struct DebugServer {
    join_handle: Option<thread::JoinHandle<()>>,
    broadcaster: ws::Sender,
    pub debug_rx: Receiver<DebugMsg>,
}

impl DebugServer {
    pub fn new(api_tx: MsgSender<ApiMsg>) -> DebugServer {
        let (debug_tx, debug_rx) = channel();

        let socket = ws::Builder::new().build(move |out| {
            Server {
                ws: out,
                debug_tx: debug_tx.clone(),
                api_tx: api_tx.clone(),
            }
        }).unwrap();

        let broadcaster = socket.broadcaster();

        let join_handle = Some(thread::spawn(move || {
            socket.listen("127.0.0.1:3583").unwrap();
        }));

        DebugServer {
            join_handle,
            broadcaster,
            debug_rx,
        }
    }
}

impl Drop for DebugServer {
    fn drop(&mut self) {
        self.broadcaster.shutdown().unwrap();
        self.join_handle.take().unwrap().join().unwrap();
    }
}

// A serializable list of debug information about passes
// that can be sent to the client.

#[derive(Serialize)]
pub enum BatchKind {
    Clip,
    Cache,
    Opaque,
    Alpha,
}

#[derive(Serialize)]
pub struct PassList {
    kind: &'static str,
    passes: Vec<Pass>,
}

impl PassList {
    pub fn new() -> PassList {
        PassList {
            kind: "passes",
            passes: Vec::new(),
        }
    }

    pub fn add(&mut self, pass: Pass) {
        self.passes.push(pass);
    }
}

#[derive(Serialize)]
pub struct Pass {
    targets: Vec<Target>,
}

impl Pass {
    pub fn new() -> Pass {
        Pass {
            targets: Vec::new(),
        }
    }

    pub fn add(&mut self, target: Target) {
        self.targets.push(target);
    }
}

#[derive(Serialize)]
pub struct Target {
    kind: &'static str,
    batches: Vec<Batch>,
}

impl Target {
    pub fn new(kind: &'static str) -> Target {
        Target {
            kind,
            batches: Vec::new(),
        }
    }

    pub fn add(&mut self, kind: BatchKind, description: &str, count: usize) {
        if count > 0 {
            self.batches.push(Batch {
                kind,
                description: description.to_owned(),
                count,
            });
        }
    }
}

#[derive(Serialize)]
struct Batch {
    kind: BatchKind,
    description: String,
    count: usize,
}
<commit_msg>Avoid panic when unable to bind websocket server address.<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::{ApiMsg, DebugCommand};
use api::channel::MsgSender;
use std::sync::mpsc::{channel, Receiver};
use std::thread;
use std::sync::mpsc::Sender;

use ws;

// Messages that are sent from the render backend to the renderer
// debug command queue. These are sent in a separate queue so
// that none of these types are exposed to the RenderApi interfaces.
// We can't use select!() as it's not stable...
pub enum DebugMsg {
    FetchPasses(ws::Sender),
}

// Represents a connection to a client.
struct Server {
    ws: ws::Sender,
    debug_tx: Sender<DebugMsg>,
    api_tx: MsgSender<ApiMsg>,
}

impl ws::Handler for Server {
    fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
        match msg {
            ws::Message::Text(string) => {
                let cmd = match string.as_str() {
                    "enable_profiler" => {
                        DebugCommand::EnableProfiler(true)
                    }
                    "disable_profiler" => {
                        DebugCommand::EnableProfiler(false)
                    }
                    "enable_texture_cache_debug" => {
                        DebugCommand::EnableTextureCacheDebug(true)
                    }
                    "disable_texture_cache_debug" => {
                        DebugCommand::EnableTextureCacheDebug(false)
                    }
                    "enable_render_target_debug" => {
                        DebugCommand::EnableRenderTargetDebug(true)
                    }
                    "disable_render_target_debug" => {
                        DebugCommand::EnableRenderTargetDebug(false)
                    }
                    "fetch_passes" => {
                        let msg = DebugMsg::FetchPasses(self.ws.clone());
                        self.debug_tx.send(msg).unwrap();
                        DebugCommand::Flush
                    }
                    msg => {
                        println!("unknown msg {}", msg);
                        return Ok(());
                    }
                };

                let msg = ApiMsg::DebugCommand(cmd);
                self.api_tx.send(msg).unwrap();
            }
            ws::Message::Binary(..) => {}
        }

        Ok(())
    }
}

// Spawn a thread for a given renderer, and wait for
// client connections.
pub struct DebugServer {
    join_handle: Option<thread::JoinHandle<()>>,
    broadcaster: ws::Sender,
    pub debug_rx: Receiver<DebugMsg>,
}

impl DebugServer {
    pub fn new(api_tx: MsgSender<ApiMsg>) -> DebugServer {
        let (debug_tx, debug_rx) = channel();

        let socket = ws::Builder::new().build(move |out| {
            Server {
                ws: out,
                debug_tx: debug_tx.clone(),
                api_tx: api_tx.clone(),
            }
        }).unwrap();

        let broadcaster = socket.broadcaster();

        let join_handle = Some(thread::spawn(move || {
            if let Err(..) = socket.listen("127.0.0.1:3583") {
                println!("ERROR: Unable to bind debugger websocket (port may be in use).");
            }
        }));

        DebugServer {
            join_handle,
            broadcaster,
            debug_rx,
        }
    }
}

impl Drop for DebugServer {
    fn drop(&mut self) {
        self.broadcaster.shutdown().ok();
        self.join_handle.take().unwrap().join().ok();
    }
}

// A serializable list of debug information about passes
// that can be sent to the client.

#[derive(Serialize)]
pub enum BatchKind {
    Clip,
    Cache,
    Opaque,
    Alpha,
}

#[derive(Serialize)]
pub struct PassList {
    kind: &'static str,
    passes: Vec<Pass>,
}

impl PassList {
    pub fn new() -> PassList {
        PassList {
            kind: "passes",
            passes: Vec::new(),
        }
    }

    pub fn add(&mut self, pass: Pass) {
        self.passes.push(pass);
    }
}

#[derive(Serialize)]
pub struct Pass {
    targets: Vec<Target>,
}

impl Pass {
    pub fn new() -> Pass {
        Pass {
            targets: Vec::new(),
        }
    }

    pub fn add(&mut self, target: Target) {
        self.targets.push(target);
    }
}

#[derive(Serialize)]
pub struct Target {
    kind: &'static str,
    batches: Vec<Batch>,
}

impl Target {
    pub fn new(kind: &'static str) -> Target {
        Target {
            kind,
            batches: Vec::new(),
        }
    }

    pub fn add(&mut self, kind: BatchKind, description: &str, count: usize) {
        if count > 0 {
            self.batches.push(Batch {
                kind,
                description: description.to_owned(),
                count,
            });
        }
    }
}

#[derive(Serialize)]
struct Batch {
    kind: BatchKind,
    description: String,
    count: usize,
}
<|endoftext|>


<commit_before>pub static HEADER: &str = include_str!("include/cxxbridge.h");

pub fn get(guard: &str) -> &'static str {
    let ifndef = format!("#ifndef {}\n", guard);
    let endif = format!("#endif // {}\n", guard);
    let begin = HEADER.find(&ifndef);
    let end = HEADER.find(&endif);
    if let (Some(begin), Some(end)) = (begin, end) {
        &HEADER[begin..end + endif.len()]
    } else {
        panic!("not found in cxxbridge.h header: {}", guard)
    }
}
<commit_msg>Accept non-\n line endings<commit_after>pub static HEADER: &str = include_str!("include/cxxbridge.h");

pub fn get(guard: &str) -> &'static str {
    let ifndef = format!("#ifndef {}", guard);
    let endif = format!("#endif // {}", guard);
    let begin = HEADER.find(&ifndef);
    let end = HEADER.find(&endif);
    if let (Some(begin), Some(end)) = (begin, end) {
        &HEADER[begin..end + endif.len()]
    } else {
        panic!("not found in cxxbridge.h header: {}", guard)
    }
}
<|endoftext|>


<commit_before>/*
* Kinds are types of type.
*
* Every type has a kind. Every type parameter has a set of kind-capabilities
* saying which kind of type may be passed as the parameter.
*
* The kinds are based on two capabilities: move and send. These may each be
* present or absent, though only three of the four combinations can actually
* occur:
*
*
*
*    MOVE +   SEND  =  "Unique": no shared substructures or pins, only
*                                interiors and ~ boxes.
*
*    MOVE + NOSEND  =  "Shared": structures containing @, fixed to the local
*                                task heap/pool; or ~ structures pointing to
*                                pinned values.
*
*  NOMOVE + NOSEND  =  "Pinned": structures directly containing resources, or
*                                by-alias closures as interior or
*                                uniquely-boxed members.
*
*  NOMOVE +   SEND  =  --      : no types are like this.
*
*
* Since this forms a lattice, we denote the capabilites in terms of a
* worst-case requirement.  That is, if your function needs to move-and-send
* (or copy) your T, you write fn<~T>(...). If you need to move but not send,
* you write fn<@T>(...). And if you need neither -- can work with any sort of
* pinned data at all -- then you write fn<T>(...).
*
* Most types are unique or shared. Other possible name combinations for these
* two: (tree, graph; pruned, pooled; message, local; owned, common) are
* plausible but nothing stands out as completely pithy-and-obvious.
*
* Pinned values arise in 2 contexts: resources and &-closures (blocks). The
* latter absolutely must not be moved, since they could escape to the heap;
* the former must not be copied, since they'd then be multiply-destructed.
* We achieve the no-copy restriction by recycling the no-move restriction
* in place on pinned kinds for &-closures; and as a benefit we can guarantee
* that a resource passed by reference to C will never move during its life,
* occasionally useful for FFI-code.
*
* Resources cannot be sent because we don't want to oblige the communication
* system to run destructors in some weird limbo context of
* messages-in-transit. It should always be ok to just free messages it's
* dropping. Even if you wanted to send them, you'd need a new sigil for the
* NOMOVE + SEND combination, and you couldn't use the move-mode library
* interface to chan.send in that case (NOMOVE after all), so the whole thing
* wouldn't really work as minimally as the encoding we have here.
*
* Note that obj~ and fn~ -- those that capture a unique environment -- can be
* sent, so satisfy ~T. So can plain obj and fn. They can all also be copied.
*
* Further notes on copying and moving; sending is accomplished by calling a
* move-in operator on something constrained to a unique type ~T.
*
*
* COPYING:
* --------
*
*   A copy is made any time you pass-by-value or execute the = operator in a
*   non-init expression. Copying requires discriminating on type constructor.
*
*   @-boxes copy shallow, copying is always legal.
*
*   ~-boxes copy deep, copying is only legal if pointee is unique-kind.
*
*     Pinned-kind values (resources, &-closures) can't be copied. All other
*     unique-kind (eg. interior) values can be copied, and copy shallow.
*
*   Note: If you have no type constructor -- only an opaque typaram -- then
*   you can only copy if the typaram is constrained to ~T; this is because @T
*   might be a "~resource" box, and making a copy would cause a deep
*   resource-copy.
*
*
* MOVING:
* -------
*
*  A move is made any time you pass-by-move (that is, with move mode '-') or
*  execute the move ('<-') or swap ('<->') operators.
*
*/

import syntax::{ast, ast_util, visit, codemap};
import std::{vec, option, str};
import ast::{kind, kind_unique, kind_shared, kind_pinned};

fn kind_lteq(a: kind, b: kind) -> bool {
    alt a {
      kind_pinned. { true }
      kind_shared. { b != kind_pinned }
      kind_unique. { b == kind_unique }
    }
}

fn lower_kind(a: kind, b: kind) -> kind {
    if kind_lteq(a, b) { a } else { b }
}

fn kind_to_str(k: kind) -> str {
    alt k {
      ast::kind_pinned. { "pinned" }
      ast::kind_unique. { "unique" }
      ast::kind_shared. { "shared" }
    }
}

fn type_and_kind(tcx: ty::ctxt, e: @ast::expr) ->
   {ty: ty::t, kind: ast::kind} {
    let t = ty::expr_ty(tcx, e);
    let k = ty::type_kind(tcx, t);
    {ty: t, kind: k}
}

fn need_expr_kind(tcx: ty::ctxt, e: @ast::expr, k_need: ast::kind,
                  descr: str) {
    let tk = type_and_kind(tcx, e);
    log #fmt["for %s: want %s type, got %s type %s", descr,
             kind_to_str(k_need), kind_to_str(tk.kind),
             util::ppaux::ty_to_str(tcx, tk.ty)];

    demand_kind(tcx, e.span, tk.ty, k_need, descr);
}

fn demand_kind(tcx: ty::ctxt, sp: codemap::span, t: ty::t,
               k_need: ast::kind, descr: str) {
    let k = ty::type_kind(tcx, t);
    if !kind_lteq(k_need, k) {
        let s =
            #fmt["mismatched kinds for %s: needed %s type, got %s type %s",
                 descr, kind_to_str(k_need), kind_to_str(k),
                 util::ppaux::ty_to_str(tcx, t)];
        tcx.sess.span_err(sp, s);
    }
}

fn need_shared_lhs_rhs(tcx: ty::ctxt, a: @ast::expr, b: @ast::expr, op: str) {
    need_expr_kind(tcx, a, ast::kind_shared, op + " lhs");
    need_expr_kind(tcx, b, ast::kind_shared, op + " rhs");
}

fn check_expr(tcx: ty::ctxt, e: @ast::expr) {
    alt e.node {

      // FIXME: These rules do not fully implement the copy type-constructor
      // discrimination described by the block comment at the top of this
      // file. This code is wrong; it lets you copy anything shared-kind.

      ast::expr_move(a, b) { need_shared_lhs_rhs(tcx, a, b, "<-"); }
      ast::expr_assign(a, b) {
        need_shared_lhs_rhs(tcx, a, b, "=");
      }
      ast::expr_assign_op(_, a, b) {
        need_shared_lhs_rhs(tcx, a, b, "op=");
      }
      ast::expr_swap(a, b) { need_shared_lhs_rhs(tcx, a, b, "<->"); }
      ast::expr_copy(a) {
        need_expr_kind(tcx, a, ast::kind_shared, "'copy' operand");
      }
      ast::expr_ret(option::some(a)) {
        need_expr_kind(tcx, a, ast::kind_shared, "'ret' operand");
      }
      ast::expr_be(a) {
        need_expr_kind(tcx, a, ast::kind_shared, "'be' operand");
      }
      ast::expr_fail(option::some(a)) {
        need_expr_kind(tcx, a, ast::kind_shared, "'fail' operand");
      }
      ast::expr_call(callee, _) {
        let tpt = ty::expr_ty_params_and_ty(tcx, callee);

        // If we have typarams, we're calling an item; we need to check
        // that all the types we're supplying as typarams conform to the
        // typaram kind constraints on that item.
        if vec::len(tpt.params) != 0u {
            let callee_def =
                ast_util::def_id_of_def(tcx.def_map.get(callee.id));
            let item_tk = ty::lookup_item_type(tcx, callee_def);
            let i = 0;
            assert (vec::len(item_tk.kinds) == vec::len(tpt.params));
            for k_need: ast::kind in item_tk.kinds {
                let t = tpt.params[i];
                demand_kind(tcx, e.span, t, k_need,
                            #fmt("typaram %d", i));
                i += 1;
            }
        }
      }
      _ { }
    }
}

fn check_stmt(tcx: ty::ctxt, stmt: @ast::stmt) {
    alt stmt.node {
      ast::stmt_decl(@{node: ast::decl_local(locals), _}, _) {
        for (let_style, local) in locals {
            alt local.node.init {
              option::some({op: ast::init_assign., expr}) {
                need_expr_kind(tcx, expr,
                               ast::kind_shared,
                               "local initializer");
              }
              option::some({op: ast::init_move., expr}) {
                // FIXME: Same as above
              }
              option::none. { /* fall through */ }
            }
        }
      }
      _ { /* fall through */ }
    }
}

fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
    let visit =
        visit::mk_simple_visitor(@{visit_expr: bind check_expr(tcx, _),
                                   visit_stmt: bind check_stmt(tcx, _)
                                      with *visit::default_simple_visitor()});
    visit::visit_crate(*crate, (), visit);
    tcx.sess.abort_if_errors();
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<commit_msg>Add more comments about kind checking on local move initializers<commit_after>/*
* Kinds are types of type.
*
* Every type has a kind. Every type parameter has a set of kind-capabilities
* saying which kind of type may be passed as the parameter.
*
* The kinds are based on two capabilities: move and send. These may each be
* present or absent, though only three of the four combinations can actually
* occur:
*
*
*
*    MOVE +   SEND  =  "Unique": no shared substructures or pins, only
*                                interiors and ~ boxes.
*
*    MOVE + NOSEND  =  "Shared": structures containing @, fixed to the local
*                                task heap/pool; or ~ structures pointing to
*                                pinned values.
*
*  NOMOVE + NOSEND  =  "Pinned": structures directly containing resources, or
*                                by-alias closures as interior or
*                                uniquely-boxed members.
*
*  NOMOVE +   SEND  =  --      : no types are like this.
*
*
* Since this forms a lattice, we denote the capabilites in terms of a
* worst-case requirement.  That is, if your function needs to move-and-send
* (or copy) your T, you write fn<~T>(...). If you need to move but not send,
* you write fn<@T>(...). And if you need neither -- can work with any sort of
* pinned data at all -- then you write fn<T>(...).
*
* Most types are unique or shared. Other possible name combinations for these
* two: (tree, graph; pruned, pooled; message, local; owned, common) are
* plausible but nothing stands out as completely pithy-and-obvious.
*
* Pinned values arise in 2 contexts: resources and &-closures (blocks). The
* latter absolutely must not be moved, since they could escape to the heap;
* the former must not be copied, since they'd then be multiply-destructed.
* We achieve the no-copy restriction by recycling the no-move restriction
* in place on pinned kinds for &-closures; and as a benefit we can guarantee
* that a resource passed by reference to C will never move during its life,
* occasionally useful for FFI-code.
*
* Resources cannot be sent because we don't want to oblige the communication
* system to run destructors in some weird limbo context of
* messages-in-transit. It should always be ok to just free messages it's
* dropping. Even if you wanted to send them, you'd need a new sigil for the
* NOMOVE + SEND combination, and you couldn't use the move-mode library
* interface to chan.send in that case (NOMOVE after all), so the whole thing
* wouldn't really work as minimally as the encoding we have here.
*
* Note that obj~ and fn~ -- those that capture a unique environment -- can be
* sent, so satisfy ~T. So can plain obj and fn. They can all also be copied.
*
* Further notes on copying and moving; sending is accomplished by calling a
* move-in operator on something constrained to a unique type ~T.
*
*
* COPYING:
* --------
*
*   A copy is made any time you pass-by-value or execute the = operator in a
*   non-init expression. Copying requires discriminating on type constructor.
*
*   @-boxes copy shallow, copying is always legal.
*
*   ~-boxes copy deep, copying is only legal if pointee is unique-kind.
*
*     Pinned-kind values (resources, &-closures) can't be copied. All other
*     unique-kind (eg. interior) values can be copied, and copy shallow.
*
*   Note: If you have no type constructor -- only an opaque typaram -- then
*   you can only copy if the typaram is constrained to ~T; this is because @T
*   might be a "~resource" box, and making a copy would cause a deep
*   resource-copy.
*
*
* MOVING:
* -------
*
*  A move is made any time you pass-by-move (that is, with move mode '-') or
*  execute the move ('<-') or swap ('<->') operators.
*
*/

import syntax::{ast, ast_util, visit, codemap};
import std::{vec, option, str};
import ast::{kind, kind_unique, kind_shared, kind_pinned};

fn kind_lteq(a: kind, b: kind) -> bool {
    alt a {
      kind_pinned. { true }
      kind_shared. { b != kind_pinned }
      kind_unique. { b == kind_unique }
    }
}

fn lower_kind(a: kind, b: kind) -> kind {
    if kind_lteq(a, b) { a } else { b }
}

fn kind_to_str(k: kind) -> str {
    alt k {
      ast::kind_pinned. { "pinned" }
      ast::kind_unique. { "unique" }
      ast::kind_shared. { "shared" }
    }
}

fn type_and_kind(tcx: ty::ctxt, e: @ast::expr) ->
   {ty: ty::t, kind: ast::kind} {
    let t = ty::expr_ty(tcx, e);
    let k = ty::type_kind(tcx, t);
    {ty: t, kind: k}
}

fn need_expr_kind(tcx: ty::ctxt, e: @ast::expr, k_need: ast::kind,
                  descr: str) {
    let tk = type_and_kind(tcx, e);
    log #fmt["for %s: want %s type, got %s type %s", descr,
             kind_to_str(k_need), kind_to_str(tk.kind),
             util::ppaux::ty_to_str(tcx, tk.ty)];

    demand_kind(tcx, e.span, tk.ty, k_need, descr);
}

fn demand_kind(tcx: ty::ctxt, sp: codemap::span, t: ty::t,
               k_need: ast::kind, descr: str) {
    let k = ty::type_kind(tcx, t);
    if !kind_lteq(k_need, k) {
        let s =
            #fmt["mismatched kinds for %s: needed %s type, got %s type %s",
                 descr, kind_to_str(k_need), kind_to_str(k),
                 util::ppaux::ty_to_str(tcx, t)];
        tcx.sess.span_err(sp, s);
    }
}

fn need_shared_lhs_rhs(tcx: ty::ctxt, a: @ast::expr, b: @ast::expr, op: str) {
    need_expr_kind(tcx, a, ast::kind_shared, op + " lhs");
    need_expr_kind(tcx, b, ast::kind_shared, op + " rhs");
}

fn check_expr(tcx: ty::ctxt, e: @ast::expr) {
    alt e.node {

      // FIXME: These rules do not fully implement the copy type-constructor
      // discrimination described by the block comment at the top of this
      // file. This code is wrong; it lets you copy anything shared-kind.

      ast::expr_move(a, b) { need_shared_lhs_rhs(tcx, a, b, "<-"); }
      ast::expr_assign(a, b) {
        need_shared_lhs_rhs(tcx, a, b, "=");
      }
      ast::expr_assign_op(_, a, b) {
        need_shared_lhs_rhs(tcx, a, b, "op=");
      }
      ast::expr_swap(a, b) { need_shared_lhs_rhs(tcx, a, b, "<->"); }
      ast::expr_copy(a) {
        need_expr_kind(tcx, a, ast::kind_shared, "'copy' operand");
      }
      ast::expr_ret(option::some(a)) {
        need_expr_kind(tcx, a, ast::kind_shared, "'ret' operand");
      }
      ast::expr_be(a) {
        need_expr_kind(tcx, a, ast::kind_shared, "'be' operand");
      }
      ast::expr_fail(option::some(a)) {
        need_expr_kind(tcx, a, ast::kind_shared, "'fail' operand");
      }
      ast::expr_call(callee, _) {
        let tpt = ty::expr_ty_params_and_ty(tcx, callee);

        // If we have typarams, we're calling an item; we need to check
        // that all the types we're supplying as typarams conform to the
        // typaram kind constraints on that item.
        if vec::len(tpt.params) != 0u {
            let callee_def =
                ast_util::def_id_of_def(tcx.def_map.get(callee.id));
            let item_tk = ty::lookup_item_type(tcx, callee_def);
            let i = 0;
            assert (vec::len(item_tk.kinds) == vec::len(tpt.params));
            for k_need: ast::kind in item_tk.kinds {
                let t = tpt.params[i];
                demand_kind(tcx, e.span, t, k_need,
                            #fmt("typaram %d", i));
                i += 1;
            }
        }
      }
      _ { }
    }
}

fn check_stmt(tcx: ty::ctxt, stmt: @ast::stmt) {
    alt stmt.node {
      ast::stmt_decl(@{node: ast::decl_local(locals), _}, _) {
        for (let_style, local) in locals {
            alt local.node.init {
              option::some({op: ast::init_assign., expr}) {
                need_expr_kind(tcx, expr,
                               ast::kind_shared,
                               "local initializer");
              }
              option::some({op: ast::init_move., expr}) {
                // FIXME: Should be as above but moving may be the
                // only way available currently to assign a resource
                // to a local.
              }
              option::none. { /* fall through */ }
            }
        }
      }
      _ { /* fall through */ }
    }
}

fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
    let visit =
        visit::mk_simple_visitor(@{visit_expr: bind check_expr(tcx, _),
                                   visit_stmt: bind check_stmt(tcx, _)
                                      with *visit::default_simple_visitor()});
    visit::visit_crate(*crate, (), visit);
    tcx.sess.abort_if_errors();
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<|endoftext|>


<commit_before>#![doc = r#"Profiling stuff.

In `bench` mode, `Profiler` is a unit structure. Also, all macros are
deactivated, so all profiling is completely removed.
"#]

#[allow(unused_imports)]
use std::time::{ Instant, Duration } ;

use common::* ;

/// Extends duration with a pretty printing.
pub trait DurationExt {
  /// Nice string representation.
  fn to_str(& self) -> String ;
}
impl DurationExt for Duration {
  fn to_str(& self) -> String {
    format!("{}.{:0>9}", self.as_secs(), self.subsec_nanos())
  }
}

/// Profile Tree.
#[derive(PartialEq, Eq)]
pub struct ProfileTree {
  /// Duration stored at this level.
  duration: Option<Duration>,
  /// Sub-branches.
  branches: HashMap<& 'static str, ProfileTree>,
}
impl ProfileTree {
  /// Tree with nothing but the top level.
  pub fn top(top: Duration) -> Self {
    ProfileTree {
      duration: Some(top),
      branches: HashMap::new(),
    }
  }

  /// Empty tree, not visible outside.
  fn empty() -> Self {
    ProfileTree { duration: None, branches: HashMap::new() }
  }

  /// Debug printing (multi-line).
  fn print(& self, pref: & 'static str, set_sum: & [ & 'static str ]) {
    self.iter(
      |scope, time, sub_time| if let Some(last) = scope.last() {
        println!(
          "; {5}{0: >1$}|- {2}s {3}{4}",
          "", 2 * scope.len(), time.to_str(), last,
          if sub_time != Duration::from_secs(0) {
            format!(" ({}s)", sub_time.to_str())
          } else {
            "".into()
          }, pref
        )
      } else {
        println!(
          "; {}total {}s{}", pref, time.to_str(),
          if sub_time != Duration::from_secs(0) {
            format!(" ({}s)", sub_time.to_str())
          } else {
            "".into()
          }
        )
      }, set_sum
    )
  }

  /// Forces a scope to be equal to the sum of its sub branches.
  ///
  /// Only legal if the scope exists and its duration is `None`.
  pub fn lift(& mut self, scope: Vec<& 'static str>) -> Res<()> {
    let mut current = self ;
    for scp in & scope {
      let tmp = current ;
      current = if let Some(current) = tmp.branches.get_mut(scp) {
        current
      } else { return Ok(()) }
    }
    let mut sum = Duration::from_secs(0) ;
    for (_, branch) in & current.branches {
      sum = sum + branch.duration.clone().unwrap_or( Duration::from_secs(0) )
    }
    if current.duration.is_some() {
      bail!( "trying to lift scope with existing duration {:?}", scope )
    }
    current.duration = Some(sum) ;
    Ok(())
  }

  /// Inserts something in the tree.
  pub fn insert(
    & mut self, scope: Vec<& 'static str>, duration: Duration
  ) {
    let (mut current, mut last_scope) = (self, "top") ;

    for scope in scope {
      let tmp = current ;
      current = tmp.branches.entry(scope).or_insert_with(
        || ProfileTree::empty()
      ) ;
      last_scope = scope
    }
    if current.duration.is_some() {
      panic!(
        "ProfileTree: trying to insert the same scope twice `{}`",
        conf.emph(last_scope)
      )
    }
    current.duration = Some(duration)
  }

  /// Iterator on the tree.
  ///
  /// Scopes are guaranteed to follow the topological order.
  pub fn iter<F>(& self, f: F, set_sum: & [& 'static str])
  where F: Fn(& [& 'static str], & Duration, Duration) {
    if let Some(duration) = self.duration.as_ref() {
      let sub_duration = self.branches.iter().fold(
        Duration::from_secs(0),
        |acc, (_, time)| acc + time.duration.unwrap_or_else(
          || Duration::from_secs(0)
        )
      ) ;
      f(&[], duration, sub_duration)
    } else {
      panic!("ProfileTree: no top duration set but already iterating")
    }
    let mut stack: Vec< (_, Vec<_>) > = vec![
      ( vec![], self.branches.iter().map(|(s, p)| (*s, p)).collect() )
    ] ;

    while let Some( (scope, mut branches) ) = stack.pop() {
      if let Some( (s, profile) ) = branches.pop() {
        let mut this_scope = scope.clone() ;
        stack.push( (scope, branches) ) ;
        this_scope.push( s ) ;
        let sub_duration = profile.branches.iter().fold(
          Duration::from_secs(0),
          |acc, (_, time)| acc + time.duration.unwrap_or_else(
            || Duration::from_secs(0)
          )
        ) ;
        if let Some(duration) = profile.duration.as_ref() {
          f(& this_scope, duration, sub_duration)
        } else {
          if set_sum.iter().any(
            |scope| s == * scope
          ) {
            let mut scope_str = "".to_string() ;
            for s in & this_scope {
              scope_str.push_str("::") ; scope_str.push_str(s)
            }
            warn!{
              "no duration for scope {}, setting to sum of branches",
              conf.emph(& scope_str)
            }
          }
          f(& this_scope, & sub_duration, sub_duration.clone())
        }
        stack.push(
          (
            this_scope,
            profile.branches.iter().map(|(s, p)| (*s, p)).collect()
          )
        )
      }
    }
  }
}

/// Maps strings to counters.
pub type Stats = HashMap<String, usize> ;
/// Provides a debug print function.
pub trait CanPrint {
  /// Debug print (multi-line).
  fn print(& self, & 'static str) ;
}
static STAT_LEN: usize = 40 ;
impl CanPrint for Stats {
  fn print(& self, pref: & 'static str) {
    let mut stats: Vec<_> = self.iter().collect() ;
    stats.sort_unstable() ;
    for (stat, count) in stats {
      if * count > 0 {
        let stat_len = ::std::cmp::min( STAT_LEN, stat.len() ) ;
        println!(
          "; {4}  {0: >1$}{2}: {3: >5}",
          "", STAT_LEN - stat_len, conf.emph(stat), count, pref
        )
      }
    }
  }
}

/// Maps scopes to
///
/// - a (start) instant option: `Some` if the scope is currently active, and
/// - a duration representing the total runtime of this scope.
pub type InstantMap = HashMap<
  Vec<& 'static str>, (Option<Instant>, Duration)
> ;

use std::cell::RefCell ;

/// Profiling structure, only in `not(bench)`.
///
/// Maintains statistics using a hashmap indexed by strings.
///
/// Internally, the structures are wrapped in `RefCell`s so that mutation
/// does not require `& mut self`.
#[cfg( not(feature = "bench") )]
pub struct Profiler {
  /// String-indexed durations.
  map: RefCell<InstantMap>,
  /// Starting tick, for total time.
  start: Instant,
  /// Other statistics.
  stats: RefCell<Stats>,
  /// Sub-profilers.
  subs: RefCell< Vec<(String, ProfileTree, Stats)> >,
}
#[cfg(feature = "bench")]
pub struct Profiler ;
impl Profiler {
  /// Constructor.
  #[cfg( not(feature = "bench") )]
  pub fn new() -> Self {
    use std::cell::RefCell ;
    Profiler {
      map: RefCell::new( HashMap::new() ),
      start: Instant::now(),
      stats: RefCell::new( HashMap::new() ),
      subs: RefCell::new( Vec::new() ),
    }
  }
  #[cfg(feature = "bench")]
  pub fn new() -> Self { Profiler }

  /// Acts on a statistic.
  #[cfg( not(feature = "bench") )]
  pub fn stat_do<F, S>(& self, stat: S, f: F)
  where F: Fn(usize) -> usize, S: Into<String> {
    let stat = stat.into() ;
    let mut map = self.stats.borrow_mut() ;
    let val = map.get(& stat).map(|n| * n).unwrap_or(0) ;
    let _ = map.insert(stat, f(val)) ;
    ()
  }

  /// Ticks.
  #[cfg( not(feature = "bench") )]
  pub fn tick(& self, scope: Vec<& 'static str>) {
    if scope.is_empty() {
      panic!("Profile: can't use scope `total`")
    }
    let mut map = self.map.borrow_mut() ;
    let time = map.entry(scope).or_insert_with(
      || ( None, Duration::from_secs(0) )
    ) ;
    time.0 = Some( Instant::now() )
  }

  /// Registers the time since the last tick.
  ///
  /// Panics if there was no tick since the last time registration.
  #[cfg( not(feature = "bench") )]
  pub fn mark(& self, scope: Vec<& 'static str>) {
    if scope.is_empty() {
      panic!("Profile: can't use scope `total`")
    }
    let mut map = self.map.borrow_mut() ;
    if let Some(
      & mut (ref mut tick, ref mut sum)
    ) = map.get_mut(& scope) {
      let mut instant = None ;
      ::std::mem::swap(& mut instant, tick) ;
      if let Some(instant) = instant {
        * sum = (* sum) + Instant::now().duration_since(instant) ;
        * tick = None
      }
    } else {
      panic!("profiling: trying to mark the time without ticking first")
    }
  }

  /// Extracts the inner hash map.
  #[cfg( not(feature = "bench") )]
  pub fn extract(& self) -> HashMap< Vec<& 'static str>, Duration > {
    let mut map = HashMap::new() ;
    for (scope, & (ref tick, ref time)) in self.map.borrow().iter() {
      if tick.is_none() {
        let _ = map.insert(scope.clone(), * time) ;
      } else {
        panic!(
          "profiling: scope `{:?}` is still live but asked to extract", scope
        )
      }
    }
    let prev = map.insert(
      vec!["total"], Instant::now().duration_since(self.start)
    ) ;
    debug_assert!( prev.is_none() ) ;
    map
  }

  /// Extracts a profile tree.
  #[cfg( not(feature = "bench") )]
  pub fn extract_tree(self) -> (ProfileTree, Stats) {
    let mut tree = ProfileTree::top(
      Instant::now().duration_since(self.start)
    ) ;
    for (
      scope, & (ref should_be_none, ref time)
    ) in self.map.borrow().iter() {
      if should_be_none.is_some() {
        warn!(
          "Profile::extract_tree: \
          still have a live instant for {:?}", scope
        )
      }
      tree.insert( scope.clone(), * time )
    }
    ( tree, self.stats.into_inner() )
  }

  /// Adds a sub-profiler.
  #[cfg( not(feature = "bench") )]
  pub fn add_sub< S: Into<String> >(
    & self, name: S, tree: ProfileTree, stats: Stats
  ) {
    self.subs.borrow_mut().push( (name.into(), tree, stats) )
  }
  #[cfg(feature = "bench")]
  pub fn add_sub(& self, _: Self) {}

  /// Consumes and prints a profiler.
  ///
  /// - `set_sum` is a slice of scopes which have no duration and will be set
  ///   to the sum of their branches (without triggering a warning)
  #[cfg( not(feature = "bench") )]
  pub fn print(self, set_sum: & [ & 'static str ]) {
    {
      let mut subs = self.subs.borrow_mut() ;
      for (name, tree, stats) in subs.drain(0..) {
        println!("; {} {{", name) ;
        tree.print("  ", set_sum) ;
        println!("; ") ;
        println!(";   stats:") ;
        stats.print("  ") ;
        println!("; }}") ;
        println!("; ")
      }
    }
    let (tree, stats) = self.extract_tree() ;
    tree.print("", set_sum) ;
    println!("; ") ;
    println!("; stats:") ;
    stats.print("") ;
  }
}<commit_msg>fixed profiler for bench<commit_after>#![doc = r#"Profiling stuff.

In `bench` mode, `Profiler` is a unit structure. Also, all macros are
deactivated, so all profiling is completely removed.
"#]

#[allow(unused_imports)]
use std::time::{ Instant, Duration } ;

use common::* ;

/// Extends duration with a pretty printing.
pub trait DurationExt {
  /// Nice string representation.
  fn to_str(& self) -> String ;
}
impl DurationExt for Duration {
  fn to_str(& self) -> String {
    format!("{}.{:0>9}", self.as_secs(), self.subsec_nanos())
  }
}

/// Profile Tree.
#[derive(PartialEq, Eq)]
pub struct ProfileTree {
  /// Duration stored at this level.
  duration: Option<Duration>,
  /// Sub-branches.
  branches: HashMap<& 'static str, ProfileTree>,
}
impl ProfileTree {
  /// Tree with nothing but the top level.
  pub fn top(top: Duration) -> Self {
    ProfileTree {
      duration: Some(top),
      branches: HashMap::new(),
    }
  }

  /// Empty tree, not visible outside.
  fn empty() -> Self {
    ProfileTree { duration: None, branches: HashMap::new() }
  }

  /// Debug printing (multi-line).
  fn print(& self, pref: & 'static str, set_sum: & [ & 'static str ]) {
    self.iter(
      |scope, time, sub_time| if let Some(last) = scope.last() {
        println!(
          "; {5}{0: >1$}|- {2}s {3}{4}",
          "", 2 * scope.len(), time.to_str(), last,
          if sub_time != Duration::from_secs(0) {
            format!(" ({}s)", sub_time.to_str())
          } else {
            "".into()
          }, pref
        )
      } else {
        println!(
          "; {}total {}s{}", pref, time.to_str(),
          if sub_time != Duration::from_secs(0) {
            format!(" ({}s)", sub_time.to_str())
          } else {
            "".into()
          }
        )
      }, set_sum
    )
  }

  /// Forces a scope to be equal to the sum of its sub branches.
  ///
  /// Only legal if the scope exists and its duration is `None`.
  pub fn lift(& mut self, scope: Vec<& 'static str>) -> Res<()> {
    let mut current = self ;
    for scp in & scope {
      let tmp = current ;
      current = if let Some(current) = tmp.branches.get_mut(scp) {
        current
      } else { return Ok(()) }
    }
    let mut sum = Duration::from_secs(0) ;
    for (_, branch) in & current.branches {
      sum = sum + branch.duration.clone().unwrap_or( Duration::from_secs(0) )
    }
    if current.duration.is_some() {
      bail!( "trying to lift scope with existing duration {:?}", scope )
    }
    current.duration = Some(sum) ;
    Ok(())
  }

  /// Inserts something in the tree.
  pub fn insert(
    & mut self, scope: Vec<& 'static str>, duration: Duration
  ) {
    let (mut current, mut last_scope) = (self, "top") ;

    for scope in scope {
      let tmp = current ;
      current = tmp.branches.entry(scope).or_insert_with(
        || ProfileTree::empty()
      ) ;
      last_scope = scope
    }
    if current.duration.is_some() {
      panic!(
        "ProfileTree: trying to insert the same scope twice `{}`",
        conf.emph(last_scope)
      )
    }
    current.duration = Some(duration)
  }

  /// Iterator on the tree.
  ///
  /// Scopes are guaranteed to follow the topological order.
  pub fn iter<F>(& self, f: F, set_sum: & [& 'static str])
  where F: Fn(& [& 'static str], & Duration, Duration) {
    if let Some(duration) = self.duration.as_ref() {
      let sub_duration = self.branches.iter().fold(
        Duration::from_secs(0),
        |acc, (_, time)| acc + time.duration.unwrap_or_else(
          || Duration::from_secs(0)
        )
      ) ;
      f(&[], duration, sub_duration)
    } else {
      panic!("ProfileTree: no top duration set but already iterating")
    }
    let mut stack: Vec< (_, Vec<_>) > = vec![
      ( vec![], self.branches.iter().map(|(s, p)| (*s, p)).collect() )
    ] ;

    while let Some( (scope, mut branches) ) = stack.pop() {
      if let Some( (s, profile) ) = branches.pop() {
        let mut this_scope = scope.clone() ;
        stack.push( (scope, branches) ) ;
        this_scope.push( s ) ;
        let sub_duration = profile.branches.iter().fold(
          Duration::from_secs(0),
          |acc, (_, time)| acc + time.duration.unwrap_or_else(
            || Duration::from_secs(0)
          )
        ) ;
        if let Some(duration) = profile.duration.as_ref() {
          f(& this_scope, duration, sub_duration)
        } else {
          if set_sum.iter().any(
            |scope| s == * scope
          ) {
            let mut scope_str = "".to_string() ;
            for s in & this_scope {
              scope_str.push_str("::") ; scope_str.push_str(s)
            }
            warn!{
              "no duration for scope {}, setting to sum of branches",
              conf.emph(& scope_str)
            }
          }
          f(& this_scope, & sub_duration, sub_duration.clone())
        }
        stack.push(
          (
            this_scope,
            profile.branches.iter().map(|(s, p)| (*s, p)).collect()
          )
        )
      }
    }
  }
}

/// Maps strings to counters.
pub type Stats = HashMap<String, usize> ;
/// Provides a debug print function.
pub trait CanPrint {
  /// Debug print (multi-line).
  fn print(& self, & 'static str) ;
}
static STAT_LEN: usize = 40 ;
impl CanPrint for Stats {
  fn print(& self, pref: & 'static str) {
    let mut stats: Vec<_> = self.iter().collect() ;
    stats.sort_unstable() ;
    for (stat, count) in stats {
      if * count > 0 {
        let stat_len = ::std::cmp::min( STAT_LEN, stat.len() ) ;
        println!(
          "; {4}  {0: >1$}{2}: {3: >5}",
          "", STAT_LEN - stat_len, conf.emph(stat), count, pref
        )
      }
    }
  }
}

/// Maps scopes to
///
/// - a (start) instant option: `Some` if the scope is currently active, and
/// - a duration representing the total runtime of this scope.
pub type InstantMap = HashMap<
  Vec<& 'static str>, (Option<Instant>, Duration)
> ;

use std::cell::RefCell ;

/// Profiling structure, only in `not(bench)`.
///
/// Maintains statistics using a hashmap indexed by strings.
///
/// Internally, the structures are wrapped in `RefCell`s so that mutation
/// does not require `& mut self`.
#[cfg( not(feature = "bench") )]
pub struct Profiler {
  /// String-indexed durations.
  map: RefCell<InstantMap>,
  /// Starting tick, for total time.
  start: Instant,
  /// Other statistics.
  stats: RefCell<Stats>,
  /// Sub-profilers.
  subs: RefCell< Vec<(String, ProfileTree, Stats)> >,
}
#[cfg(feature = "bench")]
pub struct Profiler ;
impl Profiler {
  /// Constructor.
  #[cfg( not(feature = "bench") )]
  pub fn new() -> Self {
    use std::cell::RefCell ;
    Profiler {
      map: RefCell::new( HashMap::new() ),
      start: Instant::now(),
      stats: RefCell::new( HashMap::new() ),
      subs: RefCell::new( Vec::new() ),
    }
  }
  #[cfg(feature = "bench")]
  pub fn new() -> Self { Profiler }

  /// Acts on a statistic.
  #[cfg( not(feature = "bench") )]
  pub fn stat_do<F, S>(& self, stat: S, f: F)
  where F: Fn(usize) -> usize, S: Into<String> {
    let stat = stat.into() ;
    let mut map = self.stats.borrow_mut() ;
    let val = map.get(& stat).map(|n| * n).unwrap_or(0) ;
    let _ = map.insert(stat, f(val)) ;
    ()
  }

  /// Ticks.
  #[cfg( not(feature = "bench") )]
  pub fn tick(& self, scope: Vec<& 'static str>) {
    if scope.is_empty() {
      panic!("Profile: can't use scope `total`")
    }
    let mut map = self.map.borrow_mut() ;
    let time = map.entry(scope).or_insert_with(
      || ( None, Duration::from_secs(0) )
    ) ;
    time.0 = Some( Instant::now() )
  }

  /// Registers the time since the last tick.
  ///
  /// Panics if there was no tick since the last time registration.
  #[cfg( not(feature = "bench") )]
  pub fn mark(& self, scope: Vec<& 'static str>) {
    if scope.is_empty() {
      panic!("Profile: can't use scope `total`")
    }
    let mut map = self.map.borrow_mut() ;
    if let Some(
      & mut (ref mut tick, ref mut sum)
    ) = map.get_mut(& scope) {
      let mut instant = None ;
      ::std::mem::swap(& mut instant, tick) ;
      if let Some(instant) = instant {
        * sum = (* sum) + Instant::now().duration_since(instant) ;
        * tick = None
      }
    } else {
      panic!("profiling: trying to mark the time without ticking first")
    }
  }

  /// Extracts the inner hash map.
  #[cfg( not(feature = "bench") )]
  pub fn extract(& self) -> HashMap< Vec<& 'static str>, Duration > {
    let mut map = HashMap::new() ;
    for (scope, & (ref tick, ref time)) in self.map.borrow().iter() {
      if tick.is_none() {
        let _ = map.insert(scope.clone(), * time) ;
      } else {
        panic!(
          "profiling: scope `{:?}` is still live but asked to extract", scope
        )
      }
    }
    let prev = map.insert(
      vec!["total"], Instant::now().duration_since(self.start)
    ) ;
    debug_assert!( prev.is_none() ) ;
    map
  }

  /// Extracts a profile tree.
  #[cfg( not(feature = "bench") )]
  pub fn extract_tree(self) -> (ProfileTree, Stats) {
    let mut tree = ProfileTree::top(
      Instant::now().duration_since(self.start)
    ) ;
    for (
      scope, & (ref should_be_none, ref time)
    ) in self.map.borrow().iter() {
      if should_be_none.is_some() {
        warn!(
          "Profile::extract_tree: \
          still have a live instant for {:?}", scope
        )
      }
      tree.insert( scope.clone(), * time )
    }
    ( tree, self.stats.into_inner() )
  }

  /// Adds a sub-profiler.
  #[cfg( not(feature = "bench") )]
  pub fn add_sub< S: Into<String> >(
    & self, name: S, tree: ProfileTree, stats: Stats
  ) {
    self.subs.borrow_mut().push( (name.into(), tree, stats) )
  }
  #[cfg(feature = "bench")]
  pub fn add_sub< S: Into<String> >(
    & self, _: S, _: ProfileTree, _: Stats
  ) {}

  /// Consumes and prints a profiler.
  ///
  /// - `set_sum` is a slice of scopes which have no duration and will be set
  ///   to the sum of their branches (without triggering a warning)
  #[cfg( not(feature = "bench") )]
  pub fn print(self, set_sum: & [ & 'static str ]) {
    {
      let mut subs = self.subs.borrow_mut() ;
      for (name, tree, stats) in subs.drain(0..) {
        println!("; {} {{", name) ;
        tree.print("  ", set_sum) ;
        println!("; ") ;
        println!(";   stats:") ;
        stats.print("  ") ;
        println!("; }}") ;
        println!("; ")
      }
    }
    let (tree, stats) = self.extract_tree() ;
    tree.print("", set_sum) ;
    println!("; ") ;
    println!("; stats:") ;
    stats.print("") ;
  }
}<|endoftext|>


<commit_before>use std;
use std::any::Any;
use std::boxed::Box;
use std::cell::Cell;
use std::mem::MaybeUninit;
use std::os::raw::c_void;
use std::panic::{AssertUnwindSafe, UnwindSafe, catch_unwind, resume_unwind};
use neon_runtime;
use neon_runtime::raw;
use neon_runtime::scope::Root;
use neon_runtime::try_catch::TryCatchControl;
use types::{JsObject, JsValue};
use handle::{Handle, Managed};
use object::class::ClassMap;
use result::{JsResult, NeonResult};
use super::ModuleContext;

#[cfg(feature = "legacy-runtime")]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Env(raw::Isolate);

#[cfg(feature = "napi-runtime")]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Env(raw::Env);

extern "C" fn drop_class_map(map: Box<ClassMap>) {
    std::mem::drop(map);
}

impl Env {
    #[cfg(feature = "legacy-runtime")]
    pub(crate) fn to_raw(self) -> raw::Isolate {
        let Self(ptr) = self;
        ptr
    }

    #[cfg(feature = "napi-runtime")]
    pub(crate) fn to_raw(self) -> raw::Env {
        let Self(ptr) = self;
        ptr
    }

    pub(crate) fn class_map(&mut self) -> &mut ClassMap {
        let mut ptr: *mut c_void = unsafe { neon_runtime::class::get_class_map(self.to_raw()) };
        if ptr.is_null() {
            let b: Box<ClassMap> = Box::new(ClassMap::new());
            let raw = Box::into_raw(b);
            ptr = unsafe { std::mem::transmute(raw) };
            let free_map: *mut c_void = unsafe { std::mem::transmute(drop_class_map as usize) };
            unsafe {
                neon_runtime::class::set_class_map(self.to_raw(), ptr, free_map);
            }
        }
        unsafe { std::mem::transmute(ptr) }
    }

    #[cfg(feature = "napi-runtime")]
    pub(crate) fn current() -> Env {
        panic!("Context::current() will not implemented with n-api")
    }

    #[cfg(feature = "legacy-runtime")]
    pub(crate) fn current() -> Env {
        unsafe {
            std::mem::transmute(neon_runtime::call::current_isolate())
        }
    }
}

pub struct ScopeMetadata {
    env: Env,
    active: Cell<bool>
}

pub struct Scope<'a, R: Root + 'static> {
    pub metadata: ScopeMetadata,
    pub handle_scope: &'a mut R
}

impl<'a, R: Root + 'static> Scope<'a, R> {
    pub fn with<T, F: for<'b> FnOnce(Scope<'b, R>) -> T>(env: Env, f: F) -> T {
        let mut handle_scope: R = unsafe { R::allocate() };
        unsafe {
            handle_scope.enter(env.to_raw());
        }
        let result = {
            let scope = Scope {
                metadata: ScopeMetadata {
                    env,
                    active: Cell::new(true)
                },
                handle_scope: &mut handle_scope
            };
            f(scope)
        };
        unsafe {
            handle_scope.exit(env.to_raw());
        }
        result
    }
}

pub trait ContextInternal<'a>: Sized {
    fn scope_metadata(&self) -> &ScopeMetadata;

    fn env(&self) -> Env {
        self.scope_metadata().env
    }

    fn is_active(&self) -> bool {
        self.scope_metadata().active.get()
    }

    fn check_active(&self) {
        if !self.is_active() {
            panic!("execution context is inactive");
        }
    }

    fn activate(&self) { self.scope_metadata().active.set(true); }
    fn deactivate(&self) { self.scope_metadata().active.set(false); }

    fn try_catch_internal<'b: 'a, F>(&mut self, f: F) -> Result<Handle<'a, JsValue>, Handle<'a, JsValue>>
        where F: UnwindSafe + FnOnce(&mut Self) -> JsResult<'b, JsValue>
    {
        // A closure does not have a guaranteed layout, so we need to box it in order to pass
        // a pointer to it across the boundary into C++.
        let rust_thunk = Box::into_raw(Box::new(f));

        let mut local: MaybeUninit<raw::Local> = MaybeUninit::zeroed();
        let mut unwind_value: MaybeUninit<*mut c_void> = MaybeUninit::zeroed();

        let ctrl = unsafe {
            neon_runtime::try_catch::with(try_catch_glue::<Self, F>,
                                          rust_thunk as *mut c_void,
                                          (self as *mut Self) as *mut c_void,
                                          local.as_mut_ptr(),
                                          unwind_value.as_mut_ptr())
        };

        match ctrl {
            TryCatchControl::Panicked => {
                let unwind_value: Box<dyn Any + Send> = *unsafe {
                    Box::from_raw(unwind_value.assume_init() as *mut Box<dyn Any + Send>)
                };
                resume_unwind(unwind_value);
            }
            TryCatchControl::Returned => {
                let local = unsafe { local.assume_init() };
                Ok(JsValue::new_internal(local))
            }
            TryCatchControl::Threw => {
                let local = unsafe { local.assume_init() };
                Err(JsValue::new_internal(local))
            }
        }
    }
}

extern "C" fn try_catch_glue<'a, 'b: 'a, C, F>(rust_thunk: *mut c_void,
                                               cx: *mut c_void,
                                               returned: *mut raw::Local,
                                               unwind_value: *mut *mut c_void) -> TryCatchControl
    where C: ContextInternal<'a>,
          F: UnwindSafe + FnOnce(&mut C) -> JsResult<'b, JsValue>
{
    let f: F = *unsafe { Box::from_raw(rust_thunk as *mut F) };
    let cx: &mut C = unsafe { std::mem::transmute(cx) };

    // The mutable reference to the context is a fiction of the Neon library,
    // since it doesn't actually contain any data in the Rust memory space,
    // just a link to the JS VM. So we don't need to do any kind of poisoning
    // of the context when a panic occurs. So we suppress the Rust compiler
    // errors from using the mutable reference across an unwind boundary.
    match catch_unwind(AssertUnwindSafe(|| f(cx))) {
        // No Rust panic, no JS exception.
        Ok(Ok(result)) => unsafe {
            *returned = result.to_raw();
            TryCatchControl::Returned
        }
        // No Rust panic, caught a JS exception.
        Ok(Err(_)) => {
            TryCatchControl::Threw
        }
        // Rust panicked.
        Err(err) => unsafe {
            // A panic value has an undefined layout, so wrap it in an extra box.
            let boxed = Box::new(err);
            *unwind_value = Box::into_raw(boxed) as *mut c_void;
            TryCatchControl::Panicked
        }
    }
}

#[cfg(feature = "legacy-runtime")]
pub fn initialize_module(exports: Handle<JsObject>, init: fn(ModuleContext) -> NeonResult<()>) {
    let env = Env::current();

    ModuleContext::with(env, exports, |cx| {
        let _ = init(cx);
    });
}

#[cfg(feature = "napi-runtime")]
pub fn initialize_module(env: raw::Env, exports: Handle<JsObject>, init: fn(ModuleContext) -> NeonResult<()>) {
    ModuleContext::with(Env(env), exports, |cx| {
        let _ = init(cx);
    });
}
<commit_msg>Fix N-API build.<commit_after>use std;
use std::any::Any;
use std::boxed::Box;
use std::cell::Cell;
use std::mem::MaybeUninit;
use std::os::raw::c_void;
use std::panic::{AssertUnwindSafe, UnwindSafe, catch_unwind, resume_unwind};
use neon_runtime;
use neon_runtime::raw;
use neon_runtime::scope::Root;
#[cfg(feature = "legacy-runtime")]
use neon_runtime::try_catch::TryCatchControl;
use types::{JsObject, JsValue};
use handle::{Handle, Managed};
use object::class::ClassMap;
use result::{JsResult, NeonResult};
use super::ModuleContext;

#[cfg(feature = "legacy-runtime")]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Env(raw::Isolate);

#[cfg(feature = "napi-runtime")]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Env(raw::Env);

extern "C" fn drop_class_map(map: Box<ClassMap>) {
    std::mem::drop(map);
}

impl Env {
    #[cfg(feature = "legacy-runtime")]
    pub(crate) fn to_raw(self) -> raw::Isolate {
        let Self(ptr) = self;
        ptr
    }

    #[cfg(feature = "napi-runtime")]
    pub(crate) fn to_raw(self) -> raw::Env {
        let Self(ptr) = self;
        ptr
    }

    pub(crate) fn class_map(&mut self) -> &mut ClassMap {
        let mut ptr: *mut c_void = unsafe { neon_runtime::class::get_class_map(self.to_raw()) };
        if ptr.is_null() {
            let b: Box<ClassMap> = Box::new(ClassMap::new());
            let raw = Box::into_raw(b);
            ptr = unsafe { std::mem::transmute(raw) };
            let free_map: *mut c_void = unsafe { std::mem::transmute(drop_class_map as usize) };
            unsafe {
                neon_runtime::class::set_class_map(self.to_raw(), ptr, free_map);
            }
        }
        unsafe { std::mem::transmute(ptr) }
    }

    #[cfg(feature = "napi-runtime")]
    pub(crate) fn current() -> Env {
        panic!("Context::current() will not implemented with n-api")
    }

    #[cfg(feature = "legacy-runtime")]
    pub(crate) fn current() -> Env {
        unsafe {
            std::mem::transmute(neon_runtime::call::current_isolate())
        }
    }
}

pub struct ScopeMetadata {
    env: Env,
    active: Cell<bool>
}

pub struct Scope<'a, R: Root + 'static> {
    pub metadata: ScopeMetadata,
    pub handle_scope: &'a mut R
}

impl<'a, R: Root + 'static> Scope<'a, R> {
    pub fn with<T, F: for<'b> FnOnce(Scope<'b, R>) -> T>(env: Env, f: F) -> T {
        let mut handle_scope: R = unsafe { R::allocate() };
        unsafe {
            handle_scope.enter(env.to_raw());
        }
        let result = {
            let scope = Scope {
                metadata: ScopeMetadata {
                    env,
                    active: Cell::new(true)
                },
                handle_scope: &mut handle_scope
            };
            f(scope)
        };
        unsafe {
            handle_scope.exit(env.to_raw());
        }
        result
    }
}

pub trait ContextInternal<'a>: Sized {
    fn scope_metadata(&self) -> &ScopeMetadata;

    fn env(&self) -> Env {
        self.scope_metadata().env
    }

    fn is_active(&self) -> bool {
        self.scope_metadata().active.get()
    }

    fn check_active(&self) {
        if !self.is_active() {
            panic!("execution context is inactive");
        }
    }

    fn activate(&self) { self.scope_metadata().active.set(true); }
    fn deactivate(&self) { self.scope_metadata().active.set(false); }

    #[cfg(feature = "legacy-runtime")]
    fn try_catch_internal<'b: 'a, F>(&mut self, f: F) -> Result<Handle<'a, JsValue>, Handle<'a, JsValue>>
        where F: UnwindSafe + FnOnce(&mut Self) -> JsResult<'b, JsValue>
    {
        // A closure does not have a guaranteed layout, so we need to box it in order to pass
        // a pointer to it across the boundary into C++.
        let rust_thunk = Box::into_raw(Box::new(f));

        let mut local: MaybeUninit<raw::Local> = MaybeUninit::zeroed();
        let mut unwind_value: MaybeUninit<*mut c_void> = MaybeUninit::zeroed();

        let ctrl = unsafe {
            neon_runtime::try_catch::with(try_catch_glue::<Self, F>,
                                          rust_thunk as *mut c_void,
                                          (self as *mut Self) as *mut c_void,
                                          local.as_mut_ptr(),
                                          unwind_value.as_mut_ptr())
        };

        match ctrl {
            TryCatchControl::Panicked => {
                let unwind_value: Box<dyn Any + Send> = *unsafe {
                    Box::from_raw(unwind_value.assume_init() as *mut Box<dyn Any + Send>)
                };
                resume_unwind(unwind_value);
            }
            TryCatchControl::Returned => {
                let local = unsafe { local.assume_init() };
                Ok(JsValue::new_internal(local))
            }
            TryCatchControl::Threw => {
                let local = unsafe { local.assume_init() };
                Err(JsValue::new_internal(local))
            }
        }
    }

    #[cfg(feature = "napi-runtime")]
    fn try_catch_internal<'b: 'a, F>(&mut self, f: F) -> Result<Handle<'a, JsValue>, Handle<'a, JsValue>>
        where F: UnwindSafe + FnOnce(&mut Self) -> JsResult<'b, JsValue>
    {
        unimplemented!()
    }
}

#[cfg(feature = "legacy-runtime")]
extern "C" fn try_catch_glue<'a, 'b: 'a, C, F>(rust_thunk: *mut c_void,
                                               cx: *mut c_void,
                                               returned: *mut raw::Local,
                                               unwind_value: *mut *mut c_void) -> TryCatchControl
    where C: ContextInternal<'a>,
          F: UnwindSafe + FnOnce(&mut C) -> JsResult<'b, JsValue>
{
    let f: F = *unsafe { Box::from_raw(rust_thunk as *mut F) };
    let cx: &mut C = unsafe { std::mem::transmute(cx) };

    // The mutable reference to the context is a fiction of the Neon library,
    // since it doesn't actually contain any data in the Rust memory space,
    // just a link to the JS VM. So we don't need to do any kind of poisoning
    // of the context when a panic occurs. So we suppress the Rust compiler
    // errors from using the mutable reference across an unwind boundary.
    match catch_unwind(AssertUnwindSafe(|| f(cx))) {
        // No Rust panic, no JS exception.
        Ok(Ok(result)) => unsafe {
            *returned = result.to_raw();
            TryCatchControl::Returned
        }
        // No Rust panic, caught a JS exception.
        Ok(Err(_)) => {
            TryCatchControl::Threw
        }
        // Rust panicked.
        Err(err) => unsafe {
            // A panic value has an undefined layout, so wrap it in an extra box.
            let boxed = Box::new(err);
            *unwind_value = Box::into_raw(boxed) as *mut c_void;
            TryCatchControl::Panicked
        }
    }
}

#[cfg(feature = "legacy-runtime")]
pub fn initialize_module(exports: Handle<JsObject>, init: fn(ModuleContext) -> NeonResult<()>) {
    let env = Env::current();

    ModuleContext::with(env, exports, |cx| {
        let _ = init(cx);
    });
}

#[cfg(feature = "napi-runtime")]
pub fn initialize_module(env: raw::Env, exports: Handle<JsObject>, init: fn(ModuleContext) -> NeonResult<()>) {
    ModuleContext::with(Env(env), exports, |cx| {
        let _ = init(cx);
    });
}
<|endoftext|>