<commit_before>use ffxiv::{DataCenter, World, Role, Job};

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

use serenity::builder::CreateEmbed;

use unicase::UniCase;

use std::str::FromStr;
use std::sync::Arc;

#[derive(BotCommand)]
pub struct PartyFinderCommand;

#[derive(Debug, StructOpt)]
#[structopt(about = "Create a party finder advertisement")]
pub struct Params {
  #[structopt(
    short = "m",
    long = "message",
    help = "The message of the party finder",
  )]
  message: Option<String>,
  #[structopt(
    short = "s",
    long = "size",
    help = "The size of the party finder",
    raw(possible_values = r#"&["2", "3", "4", "5", "6", "7", "8"]"#),
    raw(hide_possible_values = "true"),
  )]
  size: Option<u8>,
  #[structopt(
    short = "d",
    long = "data-center",
    help = "The data center the party finder is for",
  )]
  data_center: Option<DataCenter>,
  #[structopt(
    short = "w",
    long = "world",
    alias = "server",
    help = "The world the party finder is for (cannot be used with --data-center)",
  )]
  world: Option<World>,
  #[structopt(
    short = "i",
    long = "duty",
    help = "The duty the party finder is for",
  )]
  duty: Option<PartyFinderDuty>,
  #[structopt(
    short = "l",
    long = "min-ilvl",
    help = "The minimum item level for the party",
  )]
  min_ilvl: Option<u16>,
  #[structopt(
    short = "j",
    long = "job",
    help = "A job required in this party (can be specified multiple times)",
  )]
  jobs: Vec<Slot>,
}

impl HasParams for PartyFinderCommand {
  type Params = Params;
}

impl<'a> PublicChannelCommand<'a> for PartyFinderCommand {
  fn run(&self, _: &Context, msg: &Message, gid: GuildId, _: Arc<RwLock<GuildChannel>>, params: &[&str]) -> CommandResult<'a> {
    let params = self.params_then("partyfinder", params, |a| a
      .group(::structopt::clap::ArgGroup::with_name("location")
        .args(&["data_center", "world"])
        .required(true))
      .group(::structopt::clap::ArgGroup::with_name("size_jobs")
        .args(&["size", "jobs"])
        .required(true))
      .setting(::structopt::clap::AppSettings::ArgRequiredElseHelp))?;

    let member = gid.member(&msg.author).chain_err(|| "could not get member")?;

    let mut embed = CreateEmbed::default()
      .author(|a| a
        .name(&member.display_name())
        .icon_url(&member.user.read().face()));
    if let Some(message) = params.message {
      embed = embed.description(message);
    }
    if let Some(duty) = params.duty {
      embed = embed.field("Duty", duty.to_string(), false);
    }
    if !params.jobs.is_empty() {
      let jobs = params.jobs
        .into_iter()
        .map(|j| match j {
          Slot::All => "All".into(),
          Slot::Job(js) => js.into_iter().map(|x| ICONS[x.as_code()]).collect::<Vec<_>>().join(""),
          Slot::Role(rs) => rs.into_iter().map(|x| x.as_str()).collect::<Vec<_>>().join(""),
        })
        .collect::<Vec<_>>()
        .join("|");
      embed = embed.field("Jobs", jobs, false);
    }
    if let Some(min_ilvl) = params.min_ilvl {
      embed = embed.field("Minimum item level", min_ilvl, true);
    }
    if let Some(data_center) = params.data_center {
      embed = embed.field("Data center", data_center, true);
    }
    if let Some(world) = params.world {
      embed = embed.field("World", world, true);
    }

    if let Err(e) = msg.delete() {
      warn!("could not delete party finder command message ({}): {}", msg.id, e);
    }

    Ok(CommandSuccess::default().message(|_| embed))
  }
}

#[derive(Debug)]
enum PartyFinderDuty {
  DeltascapeV10(Difficulty),
  DeltascapeV20(Difficulty),
  DeltascapeV30(Difficulty),
  DeltascapeV40(Difficulty),

  SigmascapeV10(Difficulty),
  SigmascapeV20(Difficulty),
  SigmascapeV30(Difficulty),
  SigmascapeV40(Difficulty),
}

impl ToString for PartyFinderDuty {
  fn to_string(&self) -> String {
    if let Some(diff) = self.difficulty() {
      if *diff != Difficulty::Normal {
        return format!("{} ({})", self.base_str(), diff.as_str());
      }
    }
    self.base_str().to_string()
  }
}

impl PartyFinderDuty {
  fn base_str(&self) -> &'static str {
    match *self {
      PartyFinderDuty::DeltascapeV10(_) => "Deltascape V1.0",
      PartyFinderDuty::DeltascapeV20(_) => "Deltascape V2.0",
      PartyFinderDuty::DeltascapeV30(_) => "Deltascape V3.0",
      PartyFinderDuty::DeltascapeV40(_) => "Deltascape V4.0",
      PartyFinderDuty::SigmascapeV10(_) => "Sigmascape V1.0",
      PartyFinderDuty::SigmascapeV20(_) => "Sigmascape V2.0",
      PartyFinderDuty::SigmascapeV30(_) => "Sigmascape V3.0",
      PartyFinderDuty::SigmascapeV40(_) => "Sigmascape V4.0",
    }
  }

  fn difficulty(&self) -> Option<&Difficulty> {
    let difficulty = match *self {
      PartyFinderDuty::DeltascapeV10(ref d) => d,
      PartyFinderDuty::DeltascapeV20(ref d) => d,
      PartyFinderDuty::DeltascapeV30(ref d) => d,
      PartyFinderDuty::DeltascapeV40(ref d) => d,
      PartyFinderDuty::SigmascapeV10(ref d) => d,
      PartyFinderDuty::SigmascapeV20(ref d) => d,
      PartyFinderDuty::SigmascapeV30(ref d) => d,
      PartyFinderDuty::SigmascapeV40(ref d) => d,
    };
    Some(difficulty)
  }
}

impl FromStr for PartyFinderDuty {
  type Err = String;

  fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
    let duty = match s.to_lowercase().as_str() {
      "o1" => PartyFinderDuty::DeltascapeV10(Difficulty::Normal),
      "o2" => PartyFinderDuty::DeltascapeV20(Difficulty::Normal),
      "o3" => PartyFinderDuty::DeltascapeV30(Difficulty::Normal),
      "o4" => PartyFinderDuty::DeltascapeV40(Difficulty::Normal),
      "o5" => PartyFinderDuty::SigmascapeV10(Difficulty::Normal),
      "o6" => PartyFinderDuty::SigmascapeV20(Difficulty::Normal),
      "o7" => PartyFinderDuty::SigmascapeV30(Difficulty::Normal),
      "o8" => PartyFinderDuty::SigmascapeV40(Difficulty::Normal),

      "o1s" => PartyFinderDuty::DeltascapeV10(Difficulty::Savage),
      "o2s" => PartyFinderDuty::DeltascapeV20(Difficulty::Savage),
      "o3s" => PartyFinderDuty::DeltascapeV30(Difficulty::Savage),
      "o4s" => PartyFinderDuty::DeltascapeV40(Difficulty::Savage),
      "o5s" => PartyFinderDuty::SigmascapeV10(Difficulty::Savage),
      "o6s" => PartyFinderDuty::SigmascapeV20(Difficulty::Savage),
      "o7s" => PartyFinderDuty::SigmascapeV30(Difficulty::Savage),
      "o8s" => PartyFinderDuty::SigmascapeV40(Difficulty::Savage),

      _ => return Err(format!("Unknown duty {}", s))
    };

    Ok(duty)
  }
}

#[derive(Debug, PartialEq)]
enum Difficulty {
  Normal,
  Hard,
  Extreme,
  Savage,
  Ultimate,
}

impl Difficulty {
  fn as_str(&self) -> &'static str {
    match *self {
      Difficulty::Normal => "Normal",
      Difficulty::Hard => "Hard",
      Difficulty::Extreme => "Extreme",
      Difficulty::Savage => "Savage",
      Difficulty::Ultimate => "Ultimate",
    }
  }
}

#[derive(Debug)]
enum Slot {
  All,
  Role(Vec<Role>),
  Job(Vec<Job>)
}

impl Slot {
  fn parse_all_roles<I, S>(s: I) -> ::std::result::Result<Vec<Role>, ::ffxiv::errors::UnknownVariant>
    where I: IntoIterator<Item=S>,
          S: AsRef<str>,
  {
    let mut parts = Vec::new();
    for part in s.into_iter() {
      parts.push(Role::from_str(part.as_ref().trim())?);
    }
    Ok(parts)
  }

  fn parse_all_jobs<I, S>(s: I) -> ::std::result::Result<Vec<Job>, ::ffxiv::errors::UnknownVariant>
    where I: IntoIterator<Item=S>,
          S: AsRef<str>,
  {
    let mut parts = Vec::new();
    for part in s.into_iter() {
      parts.push(Job::from_str(part.as_ref().trim())?);
    }
    Ok(parts)
  }
}

impl FromStr for Slot {
  type Err = String;

  fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
    if UniCase::new(s) == UniCase::new("all") {
      return Ok(Slot::All);
    }

    let parts: Vec<&str> = s.split(|c| c == '+' || c == '|' || c == '/').collect();

    Slot::parse_all_roles(&parts)
      .map(Slot::Role)
      .or_else(|_| Slot::parse_all_jobs(&parts)
        .map(Slot::Job))
      .map_err(|_| "Slot was not all roles/jobs".to_string())
  }
}

lazy_static! {
  static ref ICONS: ::std::collections::HashMap<&'static str, &'static str> = {
    let mut map = ::std::collections::HashMap::new();
    map.insert("DRK", "<:drk:430347698856525826>");
    map.insert("AST", "<:ast:430347698978291712>");
    map.insert("WHM", "<:whm:430347699028754434>");
    map.insert("DRG", "<:drg:430347699032686593>");
    map.insert("MCH", "<:mch:430347699087212545>");
    map.insert("BLM", "<:blm:430347699133480970>");
    map.insert("WAR", "<:war:430347699175292929>");
    map.insert("MNK", "<:mnk:430347699213041674>");
    map.insert("BRD", "<:brd:430347699221561344>");
    map.insert("PLD", "<:pld:430347699242663948>");
    map.insert("NIN", "<:nin:430347699263373343>");
    map.insert("SAM", "<:sam:430347699326418944>");
    map.insert("SCH", "<:sch:430347699334676490>");
    map.insert("RDM", "<:rdm:430347699389464576>");
    map.insert("SMN", "<:smn:430347699443990549>");
    map
  };
}
<commit_msg>style: add newlines<commit_after>use ffxiv::{DataCenter, World, Role, Job};

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

use serenity::builder::CreateEmbed;

use unicase::UniCase;

use std::str::FromStr;
use std::sync::Arc;

#[derive(BotCommand)]
pub struct PartyFinderCommand;

#[derive(Debug, StructOpt)]
#[structopt(about = "Create a party finder advertisement")]
pub struct Params {
  #[structopt(
    short = "m",
    long = "message",
    help = "The message of the party finder",
  )]
  message: Option<String>,
  #[structopt(
    short = "s",
    long = "size",
    help = "The size of the party finder",
    raw(possible_values = r#"&["2", "3", "4", "5", "6", "7", "8"]"#),
    raw(hide_possible_values = "true"),
  )]
  size: Option<u8>,
  #[structopt(
    short = "d",
    long = "data-center",
    help = "The data center the party finder is for",
  )]
  data_center: Option<DataCenter>,
  #[structopt(
    short = "w",
    long = "world",
    alias = "server",
    help = "The world the party finder is for (cannot be used with --data-center)",
  )]
  world: Option<World>,
  #[structopt(
    short = "i",
    long = "duty",
    help = "The duty the party finder is for",
  )]
  duty: Option<PartyFinderDuty>,
  #[structopt(
    short = "l",
    long = "min-ilvl",
    help = "The minimum item level for the party",
  )]
  min_ilvl: Option<u16>,
  #[structopt(
    short = "j",
    long = "job",
    help = "A job required in this party (can be specified multiple times)",
  )]
  jobs: Vec<Slot>,
}

impl HasParams for PartyFinderCommand {
  type Params = Params;
}

impl<'a> PublicChannelCommand<'a> for PartyFinderCommand {
  fn run(&self, _: &Context, msg: &Message, gid: GuildId, _: Arc<RwLock<GuildChannel>>, params: &[&str]) -> CommandResult<'a> {
    let params = self.params_then("partyfinder", params, |a| a
      .group(::structopt::clap::ArgGroup::with_name("location")
        .args(&["data_center", "world"])
        .required(true))
      .group(::structopt::clap::ArgGroup::with_name("size_jobs")
        .args(&["size", "jobs"])
        .required(true))
      .setting(::structopt::clap::AppSettings::ArgRequiredElseHelp))?;

    let member = gid.member(&msg.author).chain_err(|| "could not get member")?;

    let mut embed = CreateEmbed::default()
      .author(|a| a
        .name(&member.display_name())
        .icon_url(&member.user.read().face()));
    if let Some(message) = params.message {
      embed = embed.description(message);
    }
    if let Some(duty) = params.duty {
      embed = embed.field("Duty", duty.to_string(), false);
    }
    if !params.jobs.is_empty() {
      let jobs = params.jobs
        .into_iter()
        .map(|j| match j {
          Slot::All => "All".into(),
          Slot::Job(js) => js.into_iter().map(|x| ICONS[x.as_code()]).collect::<Vec<_>>().join(""),
          Slot::Role(rs) => rs.into_iter().map(|x| x.as_str()).collect::<Vec<_>>().join(""),
        })
        .collect::<Vec<_>>()
        .join("|");
      embed = embed.field("Jobs", jobs, false);
    }
    if let Some(min_ilvl) = params.min_ilvl {
      embed = embed.field("Minimum item level", min_ilvl, true);
    }
    if let Some(data_center) = params.data_center {
      embed = embed.field("Data center", data_center, true);
    }
    if let Some(world) = params.world {
      embed = embed.field("World", world, true);
    }

    if let Err(e) = msg.delete() {
      warn!("could not delete party finder command message ({}): {}", msg.id, e);
    }

    Ok(CommandSuccess::default().message(|_| embed))
  }
}

#[derive(Debug)]
enum PartyFinderDuty {
  DeltascapeV10(Difficulty),
  DeltascapeV20(Difficulty),
  DeltascapeV30(Difficulty),
  DeltascapeV40(Difficulty),

  SigmascapeV10(Difficulty),
  SigmascapeV20(Difficulty),
  SigmascapeV30(Difficulty),
  SigmascapeV40(Difficulty),
}

impl ToString for PartyFinderDuty {
  fn to_string(&self) -> String {
    if let Some(diff) = self.difficulty() {
      if *diff != Difficulty::Normal {
        return format!("{} ({})", self.base_str(), diff.as_str());
      }
    }
    self.base_str().to_string()
  }
}

impl PartyFinderDuty {
  fn base_str(&self) -> &'static str {
    match *self {
      PartyFinderDuty::DeltascapeV10(_) => "Deltascape V1.0",
      PartyFinderDuty::DeltascapeV20(_) => "Deltascape V2.0",
      PartyFinderDuty::DeltascapeV30(_) => "Deltascape V3.0",
      PartyFinderDuty::DeltascapeV40(_) => "Deltascape V4.0",

      PartyFinderDuty::SigmascapeV10(_) => "Sigmascape V1.0",
      PartyFinderDuty::SigmascapeV20(_) => "Sigmascape V2.0",
      PartyFinderDuty::SigmascapeV30(_) => "Sigmascape V3.0",
      PartyFinderDuty::SigmascapeV40(_) => "Sigmascape V4.0",
    }
  }

  fn difficulty(&self) -> Option<&Difficulty> {
    let difficulty = match *self {
      PartyFinderDuty::DeltascapeV10(ref d) => d,
      PartyFinderDuty::DeltascapeV20(ref d) => d,
      PartyFinderDuty::DeltascapeV30(ref d) => d,
      PartyFinderDuty::DeltascapeV40(ref d) => d,

      PartyFinderDuty::SigmascapeV10(ref d) => d,
      PartyFinderDuty::SigmascapeV20(ref d) => d,
      PartyFinderDuty::SigmascapeV30(ref d) => d,
      PartyFinderDuty::SigmascapeV40(ref d) => d,
    };
    Some(difficulty)
  }
}

impl FromStr for PartyFinderDuty {
  type Err = String;

  fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
    let duty = match s.to_lowercase().as_str() {
      "o1" => PartyFinderDuty::DeltascapeV10(Difficulty::Normal),
      "o2" => PartyFinderDuty::DeltascapeV20(Difficulty::Normal),
      "o3" => PartyFinderDuty::DeltascapeV30(Difficulty::Normal),
      "o4" => PartyFinderDuty::DeltascapeV40(Difficulty::Normal),

      "o5" => PartyFinderDuty::SigmascapeV10(Difficulty::Normal),
      "o6" => PartyFinderDuty::SigmascapeV20(Difficulty::Normal),
      "o7" => PartyFinderDuty::SigmascapeV30(Difficulty::Normal),
      "o8" => PartyFinderDuty::SigmascapeV40(Difficulty::Normal),

      "o1s" => PartyFinderDuty::DeltascapeV10(Difficulty::Savage),
      "o2s" => PartyFinderDuty::DeltascapeV20(Difficulty::Savage),
      "o3s" => PartyFinderDuty::DeltascapeV30(Difficulty::Savage),
      "o4s" => PartyFinderDuty::DeltascapeV40(Difficulty::Savage),

      "o5s" => PartyFinderDuty::SigmascapeV10(Difficulty::Savage),
      "o6s" => PartyFinderDuty::SigmascapeV20(Difficulty::Savage),
      "o7s" => PartyFinderDuty::SigmascapeV30(Difficulty::Savage),
      "o8s" => PartyFinderDuty::SigmascapeV40(Difficulty::Savage),

      _ => return Err(format!("Unknown duty {}", s))
    };

    Ok(duty)
  }
}

#[derive(Debug, PartialEq)]
enum Difficulty {
  Normal,
  Hard,
  Extreme,
  Savage,
  Ultimate,
}

impl Difficulty {
  fn as_str(&self) -> &'static str {
    match *self {
      Difficulty::Normal => "Normal",
      Difficulty::Hard => "Hard",
      Difficulty::Extreme => "Extreme",
      Difficulty::Savage => "Savage",
      Difficulty::Ultimate => "Ultimate",
    }
  }
}

#[derive(Debug)]
enum Slot {
  All,
  Role(Vec<Role>),
  Job(Vec<Job>)
}

impl Slot {
  fn parse_all_roles<I, S>(s: I) -> ::std::result::Result<Vec<Role>, ::ffxiv::errors::UnknownVariant>
    where I: IntoIterator<Item=S>,
          S: AsRef<str>,
  {
    let mut parts = Vec::new();
    for part in s.into_iter() {
      parts.push(Role::from_str(part.as_ref().trim())?);
    }
    Ok(parts)
  }

  fn parse_all_jobs<I, S>(s: I) -> ::std::result::Result<Vec<Job>, ::ffxiv::errors::UnknownVariant>
    where I: IntoIterator<Item=S>,
          S: AsRef<str>,
  {
    let mut parts = Vec::new();
    for part in s.into_iter() {
      parts.push(Job::from_str(part.as_ref().trim())?);
    }
    Ok(parts)
  }
}

impl FromStr for Slot {
  type Err = String;

  fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
    if UniCase::new(s) == UniCase::new("all") {
      return Ok(Slot::All);
    }

    let parts: Vec<&str> = s.split(|c| c == '+' || c == '|' || c == '/').collect();

    Slot::parse_all_roles(&parts)
      .map(Slot::Role)
      .or_else(|_| Slot::parse_all_jobs(&parts)
        .map(Slot::Job))
      .map_err(|_| "Slot was not all roles/jobs".to_string())
  }
}

lazy_static! {
  static ref ICONS: ::std::collections::HashMap<&'static str, &'static str> = {
    let mut map = ::std::collections::HashMap::new();
    map.insert("DRK", "<:drk:430347698856525826>");
    map.insert("AST", "<:ast:430347698978291712>");
    map.insert("WHM", "<:whm:430347699028754434>");
    map.insert("DRG", "<:drg:430347699032686593>");
    map.insert("MCH", "<:mch:430347699087212545>");
    map.insert("BLM", "<:blm:430347699133480970>");
    map.insert("WAR", "<:war:430347699175292929>");
    map.insert("MNK", "<:mnk:430347699213041674>");
    map.insert("BRD", "<:brd:430347699221561344>");
    map.insert("PLD", "<:pld:430347699242663948>");
    map.insert("NIN", "<:nin:430347699263373343>");
    map.insert("SAM", "<:sam:430347699326418944>");
    map.insert("SCH", "<:sch:430347699334676490>");
    map.insert("RDM", "<:rdm:430347699389464576>");
    map.insert("SMN", "<:smn:430347699443990549>");
    map
  };
}
<|endoftext|>


<commit_before>use base64;
use r2pipe::{R2Pipe, R2};
use radeco_lib;
use radeco_lib::analysis::cse::cse::CSE;
use radeco_lib::analysis::interproc::fixcall::CallFixer;
use radeco_lib::analysis::sccp;
use radeco_lib::backend::lang_c::c_cfg::ctrl_flow_struct;
use radeco_lib::backend::lang_c::c_cfg::CCFGVerifier;
use radeco_lib::backend::lang_c::c_cfg_builder;
use radeco_lib::frontend::radeco_containers::*;
use radeco_lib::middle::ir_writer;
use radeco_lib::middle::regfile::SubRegisterFile;
use radeco_lib::middle::ssa::ssastorage::SSAStorage;
use radeco_lib::middle::ssa::verifier;
use radeco_lib::middle::{dce, dot};
use std::cell::RefCell;
use std::collections::HashMap;
use std::process;
use std::rc::Rc;
use std::str;
use std::sync::Arc;

thread_local!(pub static PROJ: RefCell<Option<RadecoProject>> = RefCell::new(None););

pub fn fn_list(proj: &RadecoProject) -> Vec<String> {
    proj.iter()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values())
        .map(|s| s.name.to_string())
        .collect()
}

pub fn get_function<'a>(name: &str, proj: &'a RadecoProject) -> Option<&'a RadecoFunction> {
    proj.iter()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values())
        .filter(|rfn| rfn.name == name)
        .next()
}

pub fn get_function_mut<'a>(
    name: &str,
    proj: &'a mut RadecoProject,
) -> Option<&'a mut RadecoFunction> {
    proj.iter_mut()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values_mut())
        .filter(|rfn| rfn.name == name)
        .next()
}

pub fn analyze_mod(regfile: Arc<SubRegisterFile>, rmod: &mut RadecoModule) {
    // Analyze preserved for all functions.
    {
        eprintln!("[*] Fixing Callee Information");
        let bp_name = regfile.get_name_by_alias(&"BP".to_string());
        let bp_name = bp_name.map(|s| s.to_owned());
        let sp_name = regfile.get_name_by_alias(&"SP".to_string());
        let sp_name = sp_name.map(|s| s.to_owned());
        let mut callfixer = CallFixer::new(rmod, bp_name, sp_name);
        callfixer.rounded_analysis();
    }

    // Fix call sites
    radeco_lib::analysis::functions::fix_ssa_opcalls::go(rmod);

    // Infer calling conventions
    radeco_lib::analysis::functions::infer_regusage::run(rmod, &*regfile);
}

pub fn analyze(rfn: &mut RadecoFunction) {
    eprintln!("[+] Analyzing: {} @ {:#x}", rfn.name, rfn.offset);
    {
        eprintln!("  [*] Eliminating Dead Code");
        dce::collect(rfn.ssa_mut());
    }
    let mut ssa = {
        // Constant Propagation (sccp)
        eprintln!("  [*] Propagating Constants");
        let mut analyzer = sccp::Analyzer::new(rfn.ssa_mut());
        analyzer.analyze();
        analyzer.emit_ssa()
    };
    {
        eprintln!("  [*] Eliminating More DeadCode");
        dce::collect(&mut ssa);
    }
    *rfn.ssa_mut() = ssa;
    {
        // Common SubExpression Elimination (cse)
        eprintln!("  [*] Eliminating Common SubExpressions");
        let mut cse = CSE::new(rfn.ssa_mut());
        cse.run();
    }
    {
        // Verify SSA
        eprintln!("  [*] Verifying SSA's Validity");
        match verifier::verify(rfn.ssa()) {
            Err(e) => {
                eprintln!("  [*] Found Error: {}", e);
                process::exit(255);
            }
            Ok(_) => {}
        }
    }
}

pub fn analyze_all_functions<'a>(proj: &'a mut RadecoProject) {
    let rfns = proj
        .iter_mut()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values_mut());
    for rfn in rfns {
        analyze(rfn);
    }
}

pub fn emit_ir(rfn: &RadecoFunction) -> String {
    eprintln!("  [*] Writing out IR");
    let mut res = String::new();
    ir_writer::emit_il(&mut res, Some(rfn.name.to_string()), rfn.ssa()).unwrap();
    res
}

pub fn emit_dot(ssa: &SSAStorage) -> String {
    dot::emit_dot(ssa)
}

pub fn decompile_all_functions<'a>(proj: &'a RadecoProject) -> String {
    let mut decompiled_funcs = Vec::new();
    let funcs = fn_list(&proj);
    for f in &funcs {
        match decompile(f, &proj) {
            Ok(res) => {
                decompiled_funcs.push(res);
            }
            Err(err) => {
                eprintln!("{}", err);
            }
        };
    }
    decompiled_funcs.join("\n")
}

pub fn decompile<'a>(name: &str, proj: &'a RadecoProject) -> Result<String, String> {
    if let Some(rfn) = get_function(name, &proj) {
        let rmod = proj.iter().map(|i| i.module).next().unwrap();
        let func_name_map = func_names(&rmod);
        let strings = strings(&rmod);
        decompile_priv(rfn, &func_name_map, &strings)
    } else {
        Err(format!("{} is not found.", name))
    }
}

fn decompile_priv(
    rfn: &RadecoFunction,
    func_name_map: &HashMap<u64, String>,
    strings: &HashMap<u64, String>,
) -> Result<String, String> {
    let c_cfg = c_cfg_builder::recover_c_cfg(rfn, func_name_map, strings);
    if let Err(err) = CCFGVerifier::verify(&c_cfg) {
        eprintln!("CCFG verification failed {}", err);
    }
    ctrl_flow_struct::structure_and_convert(c_cfg)
        .map(|s| s.print())
        .map_err(|e| e.to_string())
}

pub fn load_proj_by_path(path: &str) -> RadecoProject {
    let mut p = ProjectLoader::new().path(path).load();
    let regfile = p.regfile().clone();
    for mut xy in p.iter_mut() {
        analyze_mod(regfile.clone(), xy.module);
    }
    p
}

pub fn load_proj_tcp(url: &str) -> Result<RadecoProject, &'static str> {
    let r2p = R2Pipe::tcp(url)?;
    Ok(load_project_by_r2pipe(r2p))
}

pub fn load_proj_http(url: &str) -> Result<RadecoProject, &'static str> {
    let r2p = R2Pipe::http(url)?;
    Ok(load_project_by_r2pipe(r2p))
}

pub fn load_project_by_r2pipe(r2p: R2Pipe) -> RadecoProject {
    let r2 = R2::from(r2p);
    let r2w = Rc::new(RefCell::new(r2));
    let mut p = ProjectLoader::new().source(Rc::new(r2w)).load();
    let regfile = p.regfile().clone();
    for mut xy in p.iter_mut() {
        analyze_mod(regfile.clone(), xy.module);
    }
    p
}

pub fn func_names(rmod: &RadecoModule) -> HashMap<u64, String> {
    rmod.functions
        .iter()
        .map(|(&addr, f)| (addr, f.name.to_string()))
        .collect()
}

pub fn strings(rmod: &RadecoModule) -> HashMap<u64, String> {
    rmod.strings()
        .iter()
        .filter(|ref s| s.paddr.is_some() && s.string.is_some())
        .cloned()
        .map(|s| {
            let (addr, _s) = (s.vaddr.unwrap(), s.string.unwrap());
            let bytes = base64::decode(&_s).unwrap_or(Vec::new());
            let ret_string = match str::from_utf8(bytes.as_slice()) {
                Ok(v) => v.to_string(),
                Err(_e) => _s,
            };
            (addr, ret_string)
        })
        .collect()
}
<commit_msg>Never panic at core::decompile<commit_after>use base64;
use r2pipe::{R2Pipe, R2};
use radeco_lib;
use radeco_lib::analysis::cse::cse::CSE;
use radeco_lib::analysis::interproc::fixcall::CallFixer;
use radeco_lib::analysis::sccp;
use radeco_lib::backend::lang_c::c_cfg::ctrl_flow_struct;
use radeco_lib::backend::lang_c::c_cfg::CCFGVerifier;
use radeco_lib::backend::lang_c::c_cfg_builder;
use radeco_lib::frontend::radeco_containers::*;
use radeco_lib::middle::ir_writer;
use radeco_lib::middle::regfile::SubRegisterFile;
use radeco_lib::middle::ssa::ssastorage::SSAStorage;
use radeco_lib::middle::ssa::verifier;
use radeco_lib::middle::{dce, dot};
use std::cell::RefCell;
use std::collections::HashMap;
use std::panic;
use std::rc::Rc;
use std::str;
use std::sync::Arc;

thread_local!(pub static PROJ: RefCell<Option<RadecoProject>> = RefCell::new(None););

pub fn fn_list(proj: &RadecoProject) -> Vec<String> {
    proj.iter()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values())
        .map(|s| s.name.to_string())
        .collect()
}

pub fn get_function<'a>(name: &str, proj: &'a RadecoProject) -> Option<&'a RadecoFunction> {
    proj.iter()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values())
        .filter(|rfn| rfn.name == name)
        .next()
}

pub fn get_function_mut<'a>(
    name: &str,
    proj: &'a mut RadecoProject,
) -> Option<&'a mut RadecoFunction> {
    proj.iter_mut()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values_mut())
        .filter(|rfn| rfn.name == name)
        .next()
}

pub fn analyze_mod(regfile: Arc<SubRegisterFile>, rmod: &mut RadecoModule) {
    // Analyze preserved for all functions.
    {
        eprintln!("[*] Fixing Callee Information");
        let bp_name = regfile.get_name_by_alias(&"BP".to_string());
        let bp_name = bp_name.map(|s| s.to_owned());
        let sp_name = regfile.get_name_by_alias(&"SP".to_string());
        let sp_name = sp_name.map(|s| s.to_owned());
        let mut callfixer = CallFixer::new(rmod, bp_name, sp_name);
        callfixer.rounded_analysis();
    }

    // Fix call sites
    radeco_lib::analysis::functions::fix_ssa_opcalls::go(rmod);

    // Infer calling conventions
    radeco_lib::analysis::functions::infer_regusage::run(rmod, &*regfile);
}

pub fn analyze(rfn: &mut RadecoFunction) {
    eprintln!("[+] Analyzing: {} @ {:#x}", rfn.name, rfn.offset);
    {
        eprintln!("  [*] Eliminating Dead Code");
        dce::collect(rfn.ssa_mut());
    }
    let mut ssa = {
        // Constant Propagation (sccp)
        eprintln!("  [*] Propagating Constants");
        let mut analyzer = sccp::Analyzer::new(rfn.ssa_mut());
        analyzer.analyze();
        analyzer.emit_ssa()
    };
    {
        eprintln!("  [*] Eliminating More DeadCode");
        dce::collect(&mut ssa);
    }
    *rfn.ssa_mut() = ssa;
    {
        // Common SubExpression Elimination (cse)
        eprintln!("  [*] Eliminating Common SubExpressions");
        let mut cse = CSE::new(rfn.ssa_mut());
        cse.run();
    }
    {
        // Verify SSA
        eprintln!("  [*] Verifying SSA's Validity");
        match verifier::verify(rfn.ssa()) {
            Err(e) => {
                eprintln!("  [*] Found Error: {}", e);
            }
            Ok(_) => {}
        }
    }
}

pub fn analyze_all_functions<'a>(proj: &'a mut RadecoProject) {
    let rfns = proj
        .iter_mut()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values_mut());
    for rfn in rfns {
        analyze(rfn);
    }
}

pub fn emit_ir(rfn: &RadecoFunction) -> String {
    eprintln!("  [*] Writing out IR");
    let mut res = String::new();
    ir_writer::emit_il(&mut res, Some(rfn.name.to_string()), rfn.ssa()).unwrap();
    res
}

pub fn emit_dot(ssa: &SSAStorage) -> String {
    dot::emit_dot(ssa)
}

pub fn decompile_all_functions<'a>(proj: &'a RadecoProject) -> String {
    let mut decompiled_funcs = Vec::new();
    let funcs = fn_list(&proj);
    for f in &funcs {
        match decompile(f, &proj) {
            Ok(res) => {
                decompiled_funcs.push(res);
            }
            Err(err) => {
                eprintln!("{}", err);
            }
        };
    }
    decompiled_funcs.join("\n")
}

pub fn decompile<'a>(name: &str, proj: &'a RadecoProject) -> Result<String, String> {
    if let Some(rfn) = get_function(name, &proj) {
        let rmod = proj.iter().map(|i| i.module).next().unwrap();
        let func_name_map = func_names(&rmod);
        let strings = strings(&rmod);
        decompile_priv(rfn, &func_name_map, &strings)
    } else {
        Err(format!("{} is not found.", name))
    }
}

fn decompile_priv(
    rfn: &RadecoFunction,
    func_name_map: &HashMap<u64, String>,
    strings: &HashMap<u64, String>,
) -> Result<String, String> {
    let c_cfg_result =
        panic::catch_unwind(|| c_cfg_builder::recover_c_cfg(rfn, func_name_map, strings));

    if c_cfg_result.is_err() {
        return Err("Failed to recover C control flow graph".to_string());
    };

    let c_cfg = c_cfg_result.unwrap();

    if let Err(err) = CCFGVerifier::verify(&c_cfg) {
        eprintln!("CCFG verification failed {}", err);
    }

    let result = panic::catch_unwind(|| {
        ctrl_flow_struct::structure_and_convert(c_cfg.clone())
            .map(|s| s.print())
            .map_err(|e| e.to_string())
    });
    match result {
        Ok(code) => code,
        Err(_) => {
            println!("Control flow structuring failed");
            Ok(c_cfg.to_c_ast().print())
        }
    }
}

pub fn load_proj_by_path(path: &str) -> RadecoProject {
    let mut p = ProjectLoader::new().path(path).load();
    let regfile = p.regfile().clone();
    for mut xy in p.iter_mut() {
        analyze_mod(regfile.clone(), xy.module);
    }
    p
}

pub fn load_proj_tcp(url: &str) -> Result<RadecoProject, &'static str> {
    let r2p = R2Pipe::tcp(url)?;
    Ok(load_project_by_r2pipe(r2p))
}

pub fn load_proj_http(url: &str) -> Result<RadecoProject, &'static str> {
    let r2p = R2Pipe::http(url)?;
    Ok(load_project_by_r2pipe(r2p))
}

pub fn load_project_by_r2pipe(r2p: R2Pipe) -> RadecoProject {
    let r2 = R2::from(r2p);
    let r2w = Rc::new(RefCell::new(r2));
    let mut p = ProjectLoader::new().source(Rc::new(r2w)).load();
    let regfile = p.regfile().clone();
    for mut xy in p.iter_mut() {
        analyze_mod(regfile.clone(), xy.module);
    }
    p
}

pub fn func_names(rmod: &RadecoModule) -> HashMap<u64, String> {
    rmod.functions
        .iter()
        .map(|(&addr, f)| (addr, f.name.to_string()))
        .collect()
}

pub fn strings(rmod: &RadecoModule) -> HashMap<u64, String> {
    rmod.strings()
        .iter()
        .filter(|ref s| s.paddr.is_some() && s.string.is_some())
        .cloned()
        .map(|s| {
            let (addr, _s) = (s.vaddr.unwrap(), s.string.unwrap());
            let bytes = base64::decode(&_s).unwrap_or(Vec::new());
            let ret_string = match str::from_utf8(bytes.as_slice()) {
                Ok(v) => v.to_string(),
                Err(_e) => _s,
            };
            (addr, ret_string)
        })
        .collect()
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::sync::Arc;

use crate::language::sem_analysis::SemAnalysis;
use crate::language::ty::SourceType;

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct TupleId(u32);

impl From<u32> for TupleId {
    fn from(value: u32) -> TupleId {
        TupleId(value)
    }
}

impl TupleId {
    pub fn to_usize(self) -> usize {
        self.0 as usize
    }
}

pub struct Tuples {
    all: Vec<Arc<Vec<SourceType>>>,
    map: HashMap<Arc<Vec<SourceType>>, TupleId>,
}

impl Tuples {
    pub fn new() -> Tuples {
        Tuples {
            all: Vec::new(),
            map: HashMap::new(),
        }
    }

    fn get_subtypes(&self, id: TupleId) -> Arc<Vec<SourceType>> {
        self.all[id.0 as usize].clone()
    }
}

pub fn ensure_tuple(sa: &SemAnalysis, args: Vec<SourceType>) -> TupleId {
    let args = Arc::new(args);

    let mut tuples = sa.tuples.lock();

    if let Some(&tuple_id) = tuples.map.get(&args) {
        return tuple_id;
    }

    tuples.all.push(args.clone());

    let id = TupleId((tuples.all.len() - 1).try_into().unwrap());
    tuples.map.insert(args, id);

    id
}

pub fn get_tuple_subtypes(sa: &SemAnalysis, id: TupleId) -> Arc<Vec<SourceType>> {
    sa.tuples.lock().get_subtypes(id)
}
<commit_msg>use SourceTypeArray inside Tuples<commit_after>use std::collections::HashMap;
use std::sync::Arc;

use crate::language::sem_analysis::SemAnalysis;
use crate::language::ty::{SourceType, SourceTypeArray};

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct TupleId(u32);

impl From<u32> for TupleId {
    fn from(value: u32) -> TupleId {
        TupleId(value)
    }
}

impl TupleId {
    pub fn to_usize(self) -> usize {
        self.0 as usize
    }
}

pub struct Tuples {
    all: Vec<SourceTypeArray>,
    map: HashMap<SourceTypeArray, TupleId>,
}

impl Tuples {
    pub fn new() -> Tuples {
        Tuples {
            all: Vec::new(),
            map: HashMap::new(),
        }
    }

    fn get_subtypes(&self, id: TupleId) -> Arc<Vec<SourceType>> {
        let source_type_array = self.all[id.0 as usize].clone();

        match source_type_array {
            SourceTypeArray::Empty => Arc::new(Vec::new()),
            SourceTypeArray::List(data) => data,
        }
    }
}

pub fn ensure_tuple(sa: &SemAnalysis, args: Vec<SourceType>) -> TupleId {
    let mut tuples = sa.tuples.lock();
    let source_type_array = SourceTypeArray::with(args);

    if let Some(&tuple_id) = tuples.map.get(&source_type_array) {
        return tuple_id;
    }

    tuples.all.push(source_type_array.clone());

    let id = TupleId((tuples.all.len() - 1).try_into().unwrap());
    tuples.map.insert(source_type_array, id);

    id
}

pub fn get_tuple_subtypes(sa: &SemAnalysis, id: TupleId) -> Arc<Vec<SourceType>> {
    sa.tuples.lock().get_subtypes(id)
}
<|endoftext|>


<commit_before>use std::sync::atomic::{self, AtomicPtr};

use std::ptr::{self, null_mut};
use std::sync::atomic::AtomicPtr;
use std::sync::atomic::Ordering::{Relaxed, Release, Acquire};

pub struct Stack<T> {
    head: AtomicPtr<Node<T>>,
}

struct Node<T> {
    data: T,
    next: *mut Node<T>,
}

impl<T> Stack<T> {
    pub fn new() -> Stack<T> {
        Stack {
            head: AtomicPtr::new(null_mut()),
        }
    }

    pub fn push(&self, t: T) {
        // Allocate the node, and immediately turn it into a `*mut` pointer.
        let n = Box::into_raw(Box::new(Node {
            data: t,
            next: null_mut(),
        }));

        loop {
            // Snapshot current head.
            let head = self.head.load(Relaxed);

            // Update `next` pointer with snapshot.
            unsafe { (*n).next = head; }

            // If snapshot is still good, link in new node.
            if self.head.compare_and_swap(head, n, Release) == head {
                break
            }
        }
    }
}

struct RawReader {
    active: *const AtomicBool,
    ptr: *const T,
}

impl RawReader {
    unsafe fn destroy(self) {
        // Drop the atomic boolean stored on the heap.
        mem::drop_in_place(self.active);
    }
}

struct Reader<'a, T> {
    raw: RawReader,
    _marker: PhantomData<'a>,
}

impl<'a, T> Reader<'a, T> {
    fn drop(&mut self) {
        self.raw.active.store(true);
    }
}

struct Atomic<T> {
    inner: AtomicPtr<T>,
    snapshots: Stack<Box<T>>,
    readers: Stack<RawReader>,
    flags: AtomicUsize,
}

impl<T> Atomic<T> {
    fn gc(&self) {
        // Spin until no thread is currently modifying the stacks. This prevents premature frees in
        // the thread which is currently pushing to `self.readers`.
        loop {
            // Read the flags, and if no readers or garbage collectors, activate garbage
            // collection.
            let flags = self.flags.compare_and_swap(0, 1, atomic::Ordering::Relaxed);
            if flags == 0 {
                // Currently, noone accesses the readers stack and the CAS above means that the
                // lowest bitflag have been set, indicating that a garbage collection is now
                // active.
                break;
            } else if flags & 1 == 1 {
                // Another thread is currently garbage collection. No need for this thread doing
                // the same.
                return;
            }
        }

        // We've now set the lowest bit in `self.active` namely the flag defining if we are
        // collecting.

        // Initially, every snapshot is marked unused.
        let mut unused = self.snapshots.collect();

        // Traverse the readers and update the reference counts.
        self.readers.take_each(|reader| {
            if reader.active.load() {
                // The reader is not released yet, and is thus considered active.

                // Remove the reader from the unused set and insert it back into the log, as the
                // snapshot is active.
                self.snapshots.insert(unused.remove(reader.ptr).unwrap());
                // Put the reader back in the structure.
                self.readers.insert(reader);
            } else {
                // The reader was released. Destroy it.
                reader.destroy();
            }
        });

        // Unset the bitflag defining if currently garbage collecting.
        self.flags.fetch_and(!1, atomic::Ordering::Relaxed);
    }

    fn get(&self) -> Reader {
        // Increment the number of threads currently pushing to the readers stack. We add two to
        // account for the LSB being a separate bitflag. This ensures that the read snapshot isn't
        // freed while registering it in the reader stack.
        self.flags.fetch_add(2, atomic::Ordering::Relaxed);

        // Construct the raw reader.
        let reader = RawReader {
            // Load a snapshot of the pointer.
            ptr: self.inner.load(atomic::Ordering::Relaxed),
            // We allocate the atomic boolean on the heap as it is shared between the returned RAII
            // guard and the reader stack.
            released: Box::from_raw(Box::new(AtomicBool::new(false))),
        };

        // Register the reader through the reader stack, ensuring that it is not freed before the
        // RAII guard drops (`reader.release` is set to `true`).
        self.readers.push(reader);

        // Revert the original increment.
        self.flags.fetch_sub(2, atomic::Ordering::Relaxed);

        Reader {
            raw: reader,
            _marker: PhantomData,
        }
    }

    fn set(&self, new: Box<T>) {
        // Replace the inner by the new value.
        let old = self.inner.swap(Box::into_raw(new), atomic::Ordering::Relaxed);
        // Push the old pointer to the snapshot stack.
        self.snapshots.push(Box::from_raw(old));
    }
}
<commit_msg>Remove potential panic when a snapshot of a reader is not yet inserted into the snapshot set.<commit_after>use std::sync::atomic::{self, AtomicPtr};

use std::ptr::{self, null_mut};
use std::sync::atomic::AtomicPtr;
use std::sync::atomic::Ordering::{Relaxed, Release, Acquire};

pub struct Stack<T> {
    head: AtomicPtr<Node<T>>,
}

struct Node<T> {
    data: T,
    next: *mut Node<T>,
}

impl<T> Stack<T> {
    pub fn new() -> Stack<T> {
        Stack {
            head: AtomicPtr::new(null_mut()),
        }
    }

    pub fn push(&self, t: T) {
        // Allocate the node, and immediately turn it into a `*mut` pointer.
        let n = Box::into_raw(Box::new(Node {
            data: t,
            next: null_mut(),
        }));

        loop {
            // Snapshot current head.
            let head = self.head.load(Relaxed);

            // Update `next` pointer with snapshot.
            unsafe { (*n).next = head; }

            // If snapshot is still good, link in new node.
            if self.head.compare_and_swap(head, n, Release) == head {
                break
            }
        }
    }
}

struct RawReader {
    active: *const AtomicBool,
    ptr: *const T,
}

impl RawReader {
    unsafe fn destroy(self) {
        // Drop the atomic boolean stored on the heap.
        mem::drop_in_place(self.active);
    }
}

struct Reader<'a, T> {
    raw: RawReader,
    _marker: PhantomData<'a>,
}

impl<'a, T> Reader<'a, T> {
    fn drop(&mut self) {
        self.raw.active.store(true);
    }
}

struct Atomic<T> {
    inner: AtomicPtr<T>,
    snapshots: Stack<Box<T>>,
    readers: Stack<RawReader>,
    flags: AtomicUsize,
}

impl<T> Atomic<T> {
    fn gc(&self) {
        // Spin until no thread is currently modifying the stacks. This prevents premature frees in
        // the thread which is currently pushing to `self.readers`.
        loop {
            // Read the flags, and if no readers or garbage collectors, activate garbage
            // collection.
            let flags = self.flags.compare_and_swap(0, 1, atomic::Ordering::Relaxed);
            if flags == 0 {
                // Currently, noone accesses the readers stack and the CAS above means that the
                // lowest bitflag have been set, indicating that a garbage collection is now
                // active.
                break;
            } else if flags & 1 == 1 {
                // Another thread is currently garbage collection. No need for this thread doing
                // the same.
                return;
            }
        }

        // We've now set the lowest bit in `self.active` namely the flag defining if we are
        // collecting.

        // Initially, every snapshot is marked unused.
        let mut unused = self.snapshots.collect();

        // Traverse the readers and update the reference counts.
        self.readers.take_each(|reader| {
            if reader.active.load() {
                // The reader is not released yet, and is thus considered active.

                // Remove the reader from the unused set and insert it back into the log (if it
                // exists in the unused set), as the snapshot is active.
                unused.remove(reader.ptr).map(|x| self.snapshots.insert(x));
                // Put the reader back in the structure.
                self.readers.insert(reader);
            } else {
                // The reader was released. Destroy it.
                reader.destroy();
            }
        });

        // Unset the bitflag defining if currently garbage collecting.
        self.flags.fetch_and(!1, atomic::Ordering::Relaxed);
    }

    fn get(&self) -> Reader {
        // Increment the number of threads currently pushing to the readers stack. We add two to
        // account for the LSB being a separate bitflag. This ensures that the read snapshot isn't
        // freed while registering it in the reader stack.
        self.flags.fetch_add(2, atomic::Ordering::Relaxed);

        // Construct the raw reader.
        let reader = RawReader {
            // Load a snapshot of the pointer.
            ptr: self.inner.load(atomic::Ordering::Relaxed),
            // We allocate the atomic boolean on the heap as it is shared between the returned RAII
            // guard and the reader stack.
            released: Box::from_raw(Box::new(AtomicBool::new(false))),
        };

        // Register the reader through the reader stack, ensuring that it is not freed before the
        // RAII guard drops (`reader.release` is set to `true`).
        self.readers.push(reader);

        // Revert the original increment.
        self.flags.fetch_sub(2, atomic::Ordering::Relaxed);

        Reader {
            raw: reader,
            _marker: PhantomData,
        }
    }

    fn set(&self, new: Box<T>) {
        // Replace the inner by the new value.
        let old = self.inner.swap(Box::into_raw(new), atomic::Ordering::Relaxed);
        // Push the old pointer to the snapshot stack.
        self.snapshots.push(Box::from_raw(old));
    }
}
<|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 dom::bindings::codegen::Bindings::ImageDataBinding;
use dom::bindings::codegen::Bindings::ImageDataBinding::ImageDataMethods;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use euclid::size::Size2D;
use js::jsapi::{Heap, JSContext, JSObject};
use js::jsapi::{JS_GetUint8ClampedArrayData, JS_NewUint8ClampedArray};
use libc::uint8_t;
use std::default::Default;
use std::ptr;
use std::slice;
use std::vec::Vec;

#[dom_struct]
pub struct ImageData {
    reflector_: Reflector,
    width: u32,
    height: u32,
    data: Heap<*mut JSObject>,
}

impl ImageData {
    #[allow(unsafe_code)]
    pub fn new(global: GlobalRef, width: u32, height: u32, data: Option<Vec<u8>>) -> Root<ImageData> {
        let mut imagedata = box ImageData {
            reflector_: Reflector::new(),
            width: width,
            height: height,
            data: Heap::default(),
        };

        unsafe {
            let cx = global.get_cx();
            let js_object: *mut JSObject = JS_NewUint8ClampedArray(cx, width * height * 4);

            if let Some(vec) = data {
                let mut is_shared = false;
                let js_object_data: *mut uint8_t = JS_GetUint8ClampedArrayData(js_object, &mut is_shared, ptr::null());
                assert!(!is_shared);
                ptr::copy_nonoverlapping(vec.as_ptr(), js_object_data, vec.len())
            }
            (*imagedata).data.set(js_object);
        }

        reflect_dom_object(imagedata,
                           global, ImageDataBinding::Wrap)
    }

    #[allow(unsafe_code)]
    pub fn get_data_array(&self, global: &GlobalRef) -> Vec<u8> {
        unsafe {
            let cx = global.get_cx();
            let mut is_shared = false;
            let data: *const uint8_t =
                JS_GetUint8ClampedArrayData(self.Data(cx), &mut is_shared, ptr::null()) as *const uint8_t;
            assert!(!is_shared);
            let len = self.Width() * self.Height() * 4;
            slice::from_raw_parts(data, len as usize).to_vec()
        }
    }

    pub fn get_size(&self) -> Size2D<i32> {
        Size2D::new(self.Width() as i32, self.Height() as i32)
    }
}

impl ImageDataMethods for ImageData {
    // https://html.spec.whatwg.org/multipage/#dom-imagedata-width
    fn Width(&self) -> u32 {
        self.width
    }

    // https://html.spec.whatwg.org/multipage/#dom-imagedata-height
    fn Height(&self) -> u32 {
        self.height
    }

    // https://html.spec.whatwg.org/multipage/#dom-imagedata-data
    fn Data(&self, _: *mut JSContext) -> *mut JSObject {
        self.data.get()
    }
}
<commit_msg>Assert that ImageData::data is not null<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 dom::bindings::codegen::Bindings::ImageDataBinding;
use dom::bindings::codegen::Bindings::ImageDataBinding::ImageDataMethods;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use euclid::size::Size2D;
use js::jsapi::{Heap, JSContext, JSObject};
use js::jsapi::{JS_GetUint8ClampedArrayData, JS_NewUint8ClampedArray};
use libc::uint8_t;
use std::default::Default;
use std::ptr;
use std::slice;
use std::vec::Vec;

#[dom_struct]
pub struct ImageData {
    reflector_: Reflector,
    width: u32,
    height: u32,
    data: Heap<*mut JSObject>,
}

impl ImageData {
    #[allow(unsafe_code)]
    pub fn new(global: GlobalRef, width: u32, height: u32, data: Option<Vec<u8>>) -> Root<ImageData> {
        let mut imagedata = box ImageData {
            reflector_: Reflector::new(),
            width: width,
            height: height,
            data: Heap::default(),
        };

        unsafe {
            let cx = global.get_cx();
            let js_object: *mut JSObject = JS_NewUint8ClampedArray(cx, width * height * 4);
            assert!(!js_object.is_null());

            if let Some(vec) = data {
                let mut is_shared = false;
                let js_object_data: *mut uint8_t = JS_GetUint8ClampedArrayData(js_object, &mut is_shared, ptr::null());
                assert!(!is_shared);
                ptr::copy_nonoverlapping(vec.as_ptr(), js_object_data, vec.len())
            }
            (*imagedata).data.set(js_object);
        }

        reflect_dom_object(imagedata,
                           global, ImageDataBinding::Wrap)
    }

    #[allow(unsafe_code)]
    pub fn get_data_array(&self, global: &GlobalRef) -> Vec<u8> {
        unsafe {
            let cx = global.get_cx();
            let mut is_shared = false;
            let data: *const uint8_t =
                JS_GetUint8ClampedArrayData(self.Data(cx), &mut is_shared, ptr::null()) as *const uint8_t;
            assert!(!data.is_null());
            assert!(!is_shared);
            let len = self.Width() * self.Height() * 4;
            slice::from_raw_parts(data, len as usize).to_vec()
        }
    }

    pub fn get_size(&self) -> Size2D<i32> {
        Size2D::new(self.Width() as i32, self.Height() as i32)
    }
}

impl ImageDataMethods for ImageData {
    // https://html.spec.whatwg.org/multipage/#dom-imagedata-width
    fn Width(&self) -> u32 {
        self.width
    }

    // https://html.spec.whatwg.org/multipage/#dom-imagedata-height
    fn Height(&self) -> u32 {
        self.height
    }

    // https://html.spec.whatwg.org/multipage/#dom-imagedata-data
    fn Data(&self, _: *mut JSContext) -> *mut JSObject {
        assert!(!self.data.get().is_null());
        self.data.get()
    }
}
<|endoftext|>


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

#![allow(non_camel_case_types)]

use core::prelude::*;
use libc;
use local::Local;
use task::Task;

pub unsafe fn init() {
    imp::init();
}

pub unsafe fn cleanup() {
    imp::cleanup();
}

pub struct Handler {
    _data: *mut libc::c_void
}

impl Handler {
    pub unsafe fn new() -> Handler {
        imp::make_handler()
    }
}

impl Drop for Handler {
    fn drop(&mut self) {
        unsafe {
            imp::drop_handler(self);
        }
    }
}

pub unsafe fn report() {
    // See the message below for why this is not emitted to the
    // ^ Where did the message below go?
    // task's logger. This has the additional conundrum of the
    // logger may not be initialized just yet, meaning that an FFI
    // call would happen to initialized it (calling out to libuv),
    // and the FFI call needs 2MB of stack when we just ran out.

    let task: Option<*mut Task> = Local::try_unsafe_borrow();

    let name = task.and_then(|task| {
        (*task).name.as_ref().map(|n| n.as_slice())
    });

    rterrln!("\ntask '{}' has overflowed its stack", name.unwrap_or("<unknown>"));
}

// get_task_info is called from an exception / signal handler.
// It returns the guard page of the current task or 0 if that
// guard page doesn't exist. None is returned if there's currently
// no local task.
#[cfg(any(windows, target_os = "linux", target_os = "macos"))]
unsafe fn get_task_guard_page() -> Option<uint> {
    let task: Option<*mut Task> = Local::try_unsafe_borrow();
    task.map(|task| (&*task).stack_guard().unwrap_or(0))
}

#[cfg(windows)]
#[allow(non_snake_case)]
mod imp {
    use core::ptr;
    use core::mem;
    use libc;
    use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL};
    use stack;
    use super::{Handler, get_task_guard_page, report};

    // This is initialized in init() and only read from after
    static mut PAGE_SIZE: uint = 0;

    #[no_stack_check]
    extern "system" fn vectored_handler(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG {
        unsafe {
            let rec = &(*(*ExceptionInfo).ExceptionRecord);
            let code = rec.ExceptionCode;

            if code != EXCEPTION_STACK_OVERFLOW {
                return EXCEPTION_CONTINUE_SEARCH;
            }

            // We're calling into functions with stack checks,
            // however stack checks by limit should be disabled on Windows
            stack::record_sp_limit(0);

            if get_task_guard_page().is_some() {
               report();
            }

            EXCEPTION_CONTINUE_SEARCH
        }
    }

    pub unsafe fn init() {
        let mut info = mem::zeroed();
        libc::GetSystemInfo(&mut info);
        PAGE_SIZE = info.dwPageSize as uint;

        if AddVectoredExceptionHandler(0, vectored_handler) == ptr::null_mut() {
            panic!("failed to install exception handler");
        }

        mem::forget(make_handler());
    }

    pub unsafe fn cleanup() {
    }

    pub unsafe fn make_handler() -> Handler {
        if SetThreadStackGuarantee(&mut 0x5000) == 0 {
            panic!("failed to reserve stack space for exception handling");
        }

        super::Handler { _data: 0i as *mut libc::c_void }
    }

    pub unsafe fn drop_handler(_handler: &mut Handler) {
    }

    pub struct EXCEPTION_RECORD {
        pub ExceptionCode: DWORD,
        pub ExceptionFlags: DWORD,
        pub ExceptionRecord: *mut EXCEPTION_RECORD,
        pub ExceptionAddress: LPVOID,
        pub NumberParameters: DWORD,
        pub ExceptionInformation: [LPVOID, ..EXCEPTION_MAXIMUM_PARAMETERS]
    }

    pub struct EXCEPTION_POINTERS {
        pub ExceptionRecord: *mut EXCEPTION_RECORD,
        pub ContextRecord: LPVOID
    }

    pub type PVECTORED_EXCEPTION_HANDLER = extern "system"
            fn(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG;

    pub type ULONG = libc::c_ulong;

    const EXCEPTION_CONTINUE_SEARCH: LONG = 0;
    const EXCEPTION_MAXIMUM_PARAMETERS: uint = 15;
    const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd;

    extern "system" {
        fn AddVectoredExceptionHandler(FirstHandler: ULONG,
                                       VectoredHandler: PVECTORED_EXCEPTION_HANDLER)
                                      -> LPVOID;
        fn SetThreadStackGuarantee(StackSizeInBytes: *mut ULONG) -> BOOL;
    }
}

#[cfg(any(target_os = "linux", target_os = "macos"))]
mod imp {
    use core::prelude::*;
    use stack;

    use super::{Handler, get_task_guard_page, report};
    use core::mem;
    use core::ptr;
    use core::intrinsics;
    use self::signal::{siginfo, sigaction, SIGBUS, SIG_DFL,
                       SA_SIGINFO, SA_ONSTACK, sigaltstack,
                       SIGSTKSZ};
    use libc;
    use libc::funcs::posix88::mman::{mmap, munmap};
    use libc::consts::os::posix88::{SIGSEGV,
                                    PROT_READ,
                                    PROT_WRITE,
                                    MAP_PRIVATE,
                                    MAP_ANON,
                                    MAP_FAILED};

    // This is initialized in init() and only read from after
    static mut PAGE_SIZE: uint = 0;

    #[no_stack_check]
    unsafe extern fn signal_handler(signum: libc::c_int,
                                     info: *mut siginfo,
                                     _data: *mut libc::c_void) {

        // We can not return from a SIGSEGV or SIGBUS signal.
        // See: https://www.gnu.org/software/libc/manual/html_node/Handler-Returns.html

        unsafe fn term(signum: libc::c_int) -> ! {
            use core::mem::transmute;

            signal(signum, transmute(SIG_DFL));
            raise(signum);
            intrinsics::abort();
        }

        // We're calling into functions with stack checks
        stack::record_sp_limit(0);

        match get_task_guard_page() {
            Some(guard) => {
                let addr = (*info).si_addr as uint;

                if guard == 0 || addr < guard - PAGE_SIZE || addr >= guard {
                    term(signum);
                }

                report();

                intrinsics::abort()
            }
            None => term(signum)
        }
    }

    static mut MAIN_ALTSTACK: *mut libc::c_void = 0 as *mut libc::c_void;

    pub unsafe fn init() {
        let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE);
        if psize == -1 {
            panic!("failed to get page size");
        }

        PAGE_SIZE = psize as uint;

        let mut action: sigaction = mem::zeroed();
        action.sa_flags = SA_SIGINFO | SA_ONSTACK;
        action.sa_sigaction = signal_handler as sighandler_t;
        sigaction(SIGSEGV, &action, ptr::null_mut());
        sigaction(SIGBUS, &action, ptr::null_mut());

        let handler = make_handler();
        MAIN_ALTSTACK = handler._data;
        mem::forget(handler);
    }

    pub unsafe fn cleanup() {
        Handler { _data: MAIN_ALTSTACK };
    }

    pub unsafe fn make_handler() -> Handler {
        let alt_stack = mmap(ptr::null_mut(),
                             signal::SIGSTKSZ,
                             PROT_READ | PROT_WRITE,
                             MAP_PRIVATE | MAP_ANON,
                             -1,
                             0);
        if alt_stack == MAP_FAILED {
            panic!("failed to allocate an alternative stack");
        }

        let mut stack: sigaltstack = mem::zeroed();

        stack.ss_sp = alt_stack;
        stack.ss_flags = 0;
        stack.ss_size = SIGSTKSZ;

        sigaltstack(&stack, ptr::null_mut());

        Handler { _data: alt_stack }
    }

    pub unsafe fn drop_handler(handler: &mut Handler) {
        munmap(handler._data, SIGSTKSZ);
    }

    type sighandler_t = *mut libc::c_void;

    #[cfg(any(all(target_os = "linux", target_arch = "x86"), // may not match
              all(target_os = "linux", target_arch = "x86_64"),
              all(target_os = "linux", target_arch = "arm"), // may not match
              all(target_os = "linux", target_arch = "mips"), // may not match
              target_os = "android"))] // may not match
    mod signal {
        use libc;
        use super::sighandler_t;

        pub static SA_ONSTACK: libc::c_int = 0x08000000;
        pub static SA_SIGINFO: libc::c_int = 0x00000004;
        pub static SIGBUS: libc::c_int = 7;

        pub static SIGSTKSZ: libc::size_t = 8192;

        pub static SIG_DFL: sighandler_t = 0i as sighandler_t;

        // This definition is not as accurate as it could be, {si_addr} is
        // actually a giant union. Currently we're only interested in that field,
        // however.
        #[repr(C)]
        pub struct siginfo {
            si_signo: libc::c_int,
            si_errno: libc::c_int,
            si_code: libc::c_int,
            pub si_addr: *mut libc::c_void
        }

        #[repr(C)]
        pub struct sigaction {
            pub sa_sigaction: sighandler_t,
            pub sa_mask: sigset_t,
            pub sa_flags: libc::c_int,
            sa_restorer: *mut libc::c_void,
        }

        #[cfg(target_word_size = "32")]
        #[repr(C)]
        pub struct sigset_t {
            __val: [libc::c_ulong, ..32],
        }
        #[cfg(target_word_size = "64")]
        #[repr(C)]
        pub struct sigset_t {
            __val: [libc::c_ulong, ..16],
        }

        #[repr(C)]
        pub struct sigaltstack {
            pub ss_sp: *mut libc::c_void,
            pub ss_flags: libc::c_int,
            pub ss_size: libc::size_t
        }

    }

    #[cfg(target_os = "macos")]
    mod signal {
        use libc;
        use super::sighandler_t;

        pub const SA_ONSTACK: libc::c_int = 0x0001;
        pub const SA_SIGINFO: libc::c_int = 0x0040;
        pub const SIGBUS: libc::c_int = 10;

        pub const SIGSTKSZ: libc::size_t = 131072;

        pub const SIG_DFL: sighandler_t = 0i as sighandler_t;

        pub type sigset_t = u32;

        // This structure has more fields, but we're not all that interested in
        // them.
        #[repr(C)]
        pub struct siginfo {
            pub si_signo: libc::c_int,
            pub si_errno: libc::c_int,
            pub si_code: libc::c_int,
            pub pid: libc::pid_t,
            pub uid: libc::uid_t,
            pub status: libc::c_int,
            pub si_addr: *mut libc::c_void
        }

        #[repr(C)]
        pub struct sigaltstack {
            pub ss_sp: *mut libc::c_void,
            pub ss_size: libc::size_t,
            pub ss_flags: libc::c_int
        }

        #[repr(C)]
        pub struct sigaction {
            pub sa_sigaction: sighandler_t,
            pub sa_mask: sigset_t,
            pub sa_flags: libc::c_int,
        }
    }

    extern {
        pub fn signal(signum: libc::c_int, handler: sighandler_t) -> sighandler_t;
        pub fn raise(signum: libc::c_int) -> libc::c_int;

        pub fn sigaction(signum: libc::c_int,
                         act: *const sigaction,
                         oldact: *mut sigaction) -> libc::c_int;

        pub fn sigaltstack(ss: *const sigaltstack,
                           oss: *mut sigaltstack) -> libc::c_int;
    }
}

#[cfg(not(any(target_os = "linux",
              target_os = "macos",
              windows)))]
mod imp {
    use libc;

    pub unsafe fn init() {
    }

    pub unsafe fn cleanup() {
    }

    pub unsafe fn make_handler() -> super::Handler {
        super::Handler { _data: 0i as *mut libc::c_void }
    }

    pub unsafe fn drop_handler(_handler: &mut super::Handler) {
    }
}
<commit_msg>auto merge of #19366 : liigo/rust/mipsel-linux, r=alexcrichton<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(non_camel_case_types)]

use core::prelude::*;
use libc;
use local::Local;
use task::Task;

pub unsafe fn init() {
    imp::init();
}

pub unsafe fn cleanup() {
    imp::cleanup();
}

pub struct Handler {
    _data: *mut libc::c_void
}

impl Handler {
    pub unsafe fn new() -> Handler {
        imp::make_handler()
    }
}

impl Drop for Handler {
    fn drop(&mut self) {
        unsafe {
            imp::drop_handler(self);
        }
    }
}

pub unsafe fn report() {
    // See the message below for why this is not emitted to the
    // ^ Where did the message below go?
    // task's logger. This has the additional conundrum of the
    // logger may not be initialized just yet, meaning that an FFI
    // call would happen to initialized it (calling out to libuv),
    // and the FFI call needs 2MB of stack when we just ran out.

    let task: Option<*mut Task> = Local::try_unsafe_borrow();

    let name = task.and_then(|task| {
        (*task).name.as_ref().map(|n| n.as_slice())
    });

    rterrln!("\ntask '{}' has overflowed its stack", name.unwrap_or("<unknown>"));
}

// get_task_info is called from an exception / signal handler.
// It returns the guard page of the current task or 0 if that
// guard page doesn't exist. None is returned if there's currently
// no local task.
#[cfg(any(windows, target_os = "linux", target_os = "macos"))]
unsafe fn get_task_guard_page() -> Option<uint> {
    let task: Option<*mut Task> = Local::try_unsafe_borrow();
    task.map(|task| (&*task).stack_guard().unwrap_or(0))
}

#[cfg(windows)]
#[allow(non_snake_case)]
mod imp {
    use core::ptr;
    use core::mem;
    use libc;
    use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL};
    use stack;
    use super::{Handler, get_task_guard_page, report};

    // This is initialized in init() and only read from after
    static mut PAGE_SIZE: uint = 0;

    #[no_stack_check]
    extern "system" fn vectored_handler(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG {
        unsafe {
            let rec = &(*(*ExceptionInfo).ExceptionRecord);
            let code = rec.ExceptionCode;

            if code != EXCEPTION_STACK_OVERFLOW {
                return EXCEPTION_CONTINUE_SEARCH;
            }

            // We're calling into functions with stack checks,
            // however stack checks by limit should be disabled on Windows
            stack::record_sp_limit(0);

            if get_task_guard_page().is_some() {
               report();
            }

            EXCEPTION_CONTINUE_SEARCH
        }
    }

    pub unsafe fn init() {
        let mut info = mem::zeroed();
        libc::GetSystemInfo(&mut info);
        PAGE_SIZE = info.dwPageSize as uint;

        if AddVectoredExceptionHandler(0, vectored_handler) == ptr::null_mut() {
            panic!("failed to install exception handler");
        }

        mem::forget(make_handler());
    }

    pub unsafe fn cleanup() {
    }

    pub unsafe fn make_handler() -> Handler {
        if SetThreadStackGuarantee(&mut 0x5000) == 0 {
            panic!("failed to reserve stack space for exception handling");
        }

        super::Handler { _data: 0i as *mut libc::c_void }
    }

    pub unsafe fn drop_handler(_handler: &mut Handler) {
    }

    pub struct EXCEPTION_RECORD {
        pub ExceptionCode: DWORD,
        pub ExceptionFlags: DWORD,
        pub ExceptionRecord: *mut EXCEPTION_RECORD,
        pub ExceptionAddress: LPVOID,
        pub NumberParameters: DWORD,
        pub ExceptionInformation: [LPVOID, ..EXCEPTION_MAXIMUM_PARAMETERS]
    }

    pub struct EXCEPTION_POINTERS {
        pub ExceptionRecord: *mut EXCEPTION_RECORD,
        pub ContextRecord: LPVOID
    }

    pub type PVECTORED_EXCEPTION_HANDLER = extern "system"
            fn(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG;

    pub type ULONG = libc::c_ulong;

    const EXCEPTION_CONTINUE_SEARCH: LONG = 0;
    const EXCEPTION_MAXIMUM_PARAMETERS: uint = 15;
    const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd;

    extern "system" {
        fn AddVectoredExceptionHandler(FirstHandler: ULONG,
                                       VectoredHandler: PVECTORED_EXCEPTION_HANDLER)
                                      -> LPVOID;
        fn SetThreadStackGuarantee(StackSizeInBytes: *mut ULONG) -> BOOL;
    }
}

#[cfg(any(target_os = "linux", target_os = "macos"))]
mod imp {
    use core::prelude::*;
    use stack;

    use super::{Handler, get_task_guard_page, report};
    use core::mem;
    use core::ptr;
    use core::intrinsics;
    use self::signal::{siginfo, sigaction, SIGBUS, SIG_DFL,
                       SA_SIGINFO, SA_ONSTACK, sigaltstack,
                       SIGSTKSZ};
    use libc;
    use libc::funcs::posix88::mman::{mmap, munmap};
    use libc::consts::os::posix88::{SIGSEGV,
                                    PROT_READ,
                                    PROT_WRITE,
                                    MAP_PRIVATE,
                                    MAP_ANON,
                                    MAP_FAILED};

    // This is initialized in init() and only read from after
    static mut PAGE_SIZE: uint = 0;

    #[no_stack_check]
    unsafe extern fn signal_handler(signum: libc::c_int,
                                     info: *mut siginfo,
                                     _data: *mut libc::c_void) {

        // We can not return from a SIGSEGV or SIGBUS signal.
        // See: https://www.gnu.org/software/libc/manual/html_node/Handler-Returns.html

        unsafe fn term(signum: libc::c_int) -> ! {
            use core::mem::transmute;

            signal(signum, transmute(SIG_DFL));
            raise(signum);
            intrinsics::abort();
        }

        // We're calling into functions with stack checks
        stack::record_sp_limit(0);

        match get_task_guard_page() {
            Some(guard) => {
                let addr = (*info).si_addr as uint;

                if guard == 0 || addr < guard - PAGE_SIZE || addr >= guard {
                    term(signum);
                }

                report();

                intrinsics::abort()
            }
            None => term(signum)
        }
    }

    static mut MAIN_ALTSTACK: *mut libc::c_void = 0 as *mut libc::c_void;

    pub unsafe fn init() {
        let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE);
        if psize == -1 {
            panic!("failed to get page size");
        }

        PAGE_SIZE = psize as uint;

        let mut action: sigaction = mem::zeroed();
        action.sa_flags = SA_SIGINFO | SA_ONSTACK;
        action.sa_sigaction = signal_handler as sighandler_t;
        sigaction(SIGSEGV, &action, ptr::null_mut());
        sigaction(SIGBUS, &action, ptr::null_mut());

        let handler = make_handler();
        MAIN_ALTSTACK = handler._data;
        mem::forget(handler);
    }

    pub unsafe fn cleanup() {
        Handler { _data: MAIN_ALTSTACK };
    }

    pub unsafe fn make_handler() -> Handler {
        let alt_stack = mmap(ptr::null_mut(),
                             signal::SIGSTKSZ,
                             PROT_READ | PROT_WRITE,
                             MAP_PRIVATE | MAP_ANON,
                             -1,
                             0);
        if alt_stack == MAP_FAILED {
            panic!("failed to allocate an alternative stack");
        }

        let mut stack: sigaltstack = mem::zeroed();

        stack.ss_sp = alt_stack;
        stack.ss_flags = 0;
        stack.ss_size = SIGSTKSZ;

        sigaltstack(&stack, ptr::null_mut());

        Handler { _data: alt_stack }
    }

    pub unsafe fn drop_handler(handler: &mut Handler) {
        munmap(handler._data, SIGSTKSZ);
    }

    type sighandler_t = *mut libc::c_void;

    #[cfg(any(all(target_os = "linux", target_arch = "x86"), // may not match
              all(target_os = "linux", target_arch = "x86_64"),
              all(target_os = "linux", target_arch = "arm"), // may not match
              all(target_os = "linux", target_arch = "mips"), // may not match
              all(target_os = "linux", target_arch = "mipsel"), // may not match
              target_os = "android"))] // may not match
    mod signal {
        use libc;
        use super::sighandler_t;

        pub static SA_ONSTACK: libc::c_int = 0x08000000;
        pub static SA_SIGINFO: libc::c_int = 0x00000004;
        pub static SIGBUS: libc::c_int = 7;

        pub static SIGSTKSZ: libc::size_t = 8192;

        pub static SIG_DFL: sighandler_t = 0i as sighandler_t;

        // This definition is not as accurate as it could be, {si_addr} is
        // actually a giant union. Currently we're only interested in that field,
        // however.
        #[repr(C)]
        pub struct siginfo {
            si_signo: libc::c_int,
            si_errno: libc::c_int,
            si_code: libc::c_int,
            pub si_addr: *mut libc::c_void
        }

        #[repr(C)]
        pub struct sigaction {
            pub sa_sigaction: sighandler_t,
            pub sa_mask: sigset_t,
            pub sa_flags: libc::c_int,
            sa_restorer: *mut libc::c_void,
        }

        #[cfg(target_word_size = "32")]
        #[repr(C)]
        pub struct sigset_t {
            __val: [libc::c_ulong, ..32],
        }
        #[cfg(target_word_size = "64")]
        #[repr(C)]
        pub struct sigset_t {
            __val: [libc::c_ulong, ..16],
        }

        #[repr(C)]
        pub struct sigaltstack {
            pub ss_sp: *mut libc::c_void,
            pub ss_flags: libc::c_int,
            pub ss_size: libc::size_t
        }

    }

    #[cfg(target_os = "macos")]
    mod signal {
        use libc;
        use super::sighandler_t;

        pub const SA_ONSTACK: libc::c_int = 0x0001;
        pub const SA_SIGINFO: libc::c_int = 0x0040;
        pub const SIGBUS: libc::c_int = 10;

        pub const SIGSTKSZ: libc::size_t = 131072;

        pub const SIG_DFL: sighandler_t = 0i as sighandler_t;

        pub type sigset_t = u32;

        // This structure has more fields, but we're not all that interested in
        // them.
        #[repr(C)]
        pub struct siginfo {
            pub si_signo: libc::c_int,
            pub si_errno: libc::c_int,
            pub si_code: libc::c_int,
            pub pid: libc::pid_t,
            pub uid: libc::uid_t,
            pub status: libc::c_int,
            pub si_addr: *mut libc::c_void
        }

        #[repr(C)]
        pub struct sigaltstack {
            pub ss_sp: *mut libc::c_void,
            pub ss_size: libc::size_t,
            pub ss_flags: libc::c_int
        }

        #[repr(C)]
        pub struct sigaction {
            pub sa_sigaction: sighandler_t,
            pub sa_mask: sigset_t,
            pub sa_flags: libc::c_int,
        }
    }

    extern {
        pub fn signal(signum: libc::c_int, handler: sighandler_t) -> sighandler_t;
        pub fn raise(signum: libc::c_int) -> libc::c_int;

        pub fn sigaction(signum: libc::c_int,
                         act: *const sigaction,
                         oldact: *mut sigaction) -> libc::c_int;

        pub fn sigaltstack(ss: *const sigaltstack,
                           oss: *mut sigaltstack) -> libc::c_int;
    }
}

#[cfg(not(any(target_os = "linux",
              target_os = "macos",
              windows)))]
mod imp {
    use libc;

    pub unsafe fn init() {
    }

    pub unsafe fn cleanup() {
    }

    pub unsafe fn make_handler() -> super::Handler {
        super::Handler { _data: 0i as *mut libc::c_void }
    }

    pub unsafe fn drop_handler(_handler: &mut super::Handler) {
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use crate::ffi::CStr;
use crate::io;
use crate::mem;
use crate::sys::hermit::abi;
use crate::sys::hermit::fast_thread_local::run_dtors;
use crate::time::Duration;

pub type Tid = abi::Tid;

pub struct Thread {
    tid: Tid,
}

unsafe impl Send for Thread {}
unsafe impl Sync for Thread {}

pub const DEFAULT_MIN_STACK_SIZE: usize = 1_048_576;

impl Thread {
    pub unsafe fn new_with_coreid(
        stack: usize,
        p: Box<dyn FnOnce()>,
        core_id: isize,
    ) -> io::Result<Thread> {
        let p = Box::into_raw(box p);
        let tid = abi::spawn2(
            thread_start,
            p as usize,
            abi::Priority::into(abi::NORMAL_PRIO),
            stack,
            core_id,
        );

        return if tid == 0 {
            // The thread failed to start and as a result p was not consumed. Therefore, it is
            // safe to reconstruct the box so that it gets deallocated.
            drop(Box::from_raw(p));
            Err(io::Error::new(io::ErrorKind::Other, "Unable to create thread!"))
        } else {
            Ok(Thread { tid: tid })
        };

        extern "C" fn thread_start(main: usize) {
            unsafe {
                // Finally, let's run some code.
                Box::from_raw(main as *mut Box<dyn FnOnce()>)();

                // run all destructors
                run_dtors();
            }
        }
    }

    pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
        Thread::new_with_coreid(stack, p, -1 /* = no specific core */)
    }

    #[inline]
    pub fn yield_now() {
        unsafe {
            abi::yield_now();
        }
    }

    #[inline]
    pub fn set_name(_name: &CStr) {
        // nope
    }

    #[inline]
    pub fn sleep(dur: Duration) {
        unsafe {
            abi::usleep(dur.as_micros() as u64);
        }
    }

    pub fn join(self) {
        unsafe {
            let _ = abi::join(self.tid);
        }
    }

    #[inline]
    pub fn id(&self) -> Tid {
        self.tid
    }

    #[inline]
    pub fn into_id(self) -> Tid {
        let id = self.tid;
        mem::forget(self);
        id
    }
}

pub mod guard {
    pub type Guard = !;
    pub unsafe fn current() -> Option<Guard> {
        None
    }
    pub unsafe fn init() -> Option<Guard> {
        None
    }
}
<commit_msg>use nicer code style to define DEFAULT_MIN_STACK_SIZE<commit_after>#![allow(dead_code)]

use crate::ffi::CStr;
use crate::io;
use crate::mem;
use crate::sys::hermit::abi;
use crate::sys::hermit::fast_thread_local::run_dtors;
use crate::time::Duration;

pub type Tid = abi::Tid;

pub struct Thread {
    tid: Tid,
}

unsafe impl Send for Thread {}
unsafe impl Sync for Thread {}

pub const DEFAULT_MIN_STACK_SIZE: usize = 1 << 20;

impl Thread {
    pub unsafe fn new_with_coreid(
        stack: usize,
        p: Box<dyn FnOnce()>,
        core_id: isize,
    ) -> io::Result<Thread> {
        let p = Box::into_raw(box p);
        let tid = abi::spawn2(
            thread_start,
            p as usize,
            abi::Priority::into(abi::NORMAL_PRIO),
            stack,
            core_id,
        );

        return if tid == 0 {
            // The thread failed to start and as a result p was not consumed. Therefore, it is
            // safe to reconstruct the box so that it gets deallocated.
            drop(Box::from_raw(p));
            Err(io::Error::new(io::ErrorKind::Other, "Unable to create thread!"))
        } else {
            Ok(Thread { tid: tid })
        };

        extern "C" fn thread_start(main: usize) {
            unsafe {
                // Finally, let's run some code.
                Box::from_raw(main as *mut Box<dyn FnOnce()>)();

                // run all destructors
                run_dtors();
            }
        }
    }

    pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
        Thread::new_with_coreid(stack, p, -1 /* = no specific core */)
    }

    #[inline]
    pub fn yield_now() {
        unsafe {
            abi::yield_now();
        }
    }

    #[inline]
    pub fn set_name(_name: &CStr) {
        // nope
    }

    #[inline]
    pub fn sleep(dur: Duration) {
        unsafe {
            abi::usleep(dur.as_micros() as u64);
        }
    }

    pub fn join(self) {
        unsafe {
            let _ = abi::join(self.tid);
        }
    }

    #[inline]
    pub fn id(&self) -> Tid {
        self.tid
    }

    #[inline]
    pub fn into_id(self) -> Tid {
        let id = self.tid;
        mem::forget(self);
        id
    }
}

pub mod guard {
    pub type Guard = !;
    pub unsafe fn current() -> Option<Guard> {
        None
    }
    pub unsafe fn init() -> Option<Guard> {
        None
    }
}
<|endoftext|>


<commit_before>#![feature(test)]

extern crate test;
extern crate rand;
extern crate presort;

use test::Bencher;
use rand::Rng;

use presort::PresortedVec;
use presort::inc_tree::{Tree, IncTree, dump, update};

const NODES: usize = 10000;
const BINARY_DEPTH: usize = 13;
const DEEP_DEPTH: usize = 150;
const WIDE_DEPTH: usize = 6;
const DEPTH: usize = BINARY_DEPTH;

//creates a random tree with the requested parameters
fn build_tree(max_depth: usize, nodes: usize) -> Tree<usize> {
    let mut rng = rand::thread_rng();

    let tree = Tree::new_node(rng.gen());

    for _ in 0..nodes {
        let mut branch = tree.clone();
        let depth = rng.gen::<usize>() % max_depth;
        for _ in 0..depth {
            let num = branch.num_children();
            if num <= 0 {break};
            branch = branch.get_child(rng.gen::<usize>() % num);
        }
        branch.push_child(Tree::new_node(rng.gen()));
    }

    tree
}

// Pick a random subtree from the tree
fn random_subtree(tree: &Tree<usize>, high_depth: usize) -> Tree<usize> {
    let mut rng = rand::thread_rng();
    let mut subtree = tree.clone();
    let depth = rng.gen::<usize>() % high_depth;
    for _ in 0..depth {
        let num = subtree.num_children();
        if num <= 0 { return subtree; }
        subtree = subtree.get_child(rng.gen::<usize>() % num);
    }
    return subtree;
}

// mutates `edits` number of values of `tree`,
// at no more than a max depth of `high_depth`
// shorter branches may have the ends updated more often
fn mutate_vals(tree: &Tree<usize>, high_depth: usize, edits: usize) {
    let mut rng = rand::thread_rng();
    for _ in 0..edits {
        random_subtree(tree, high_depth).set_data(rng.gen());
    }
}

fn incr_vals(tree: &Tree<usize>, high_depth: usize, edits: usize) {
    for _ in 0..edits {
        let subtree = random_subtree(tree, high_depth);
        subtree.set_data(subtree.get_data() + 1);
    }
}

fn add_branches(tree: &Tree<usize>, high_depth: usize, adds: usize) {
    let mut rng = rand::thread_rng();
    for _ in 0..adds {
        random_subtree(tree, high_depth).push_child(Tree::new_node(rng.gen()));
    }
}

#[bench]
fn edit_50_batch(b: &mut Bencher) {
    let mut vec = PresortedVec::new();
    let tree = build_tree(DEPTH, NODES);
    dump(&tree, &mut vec);
    b.iter(|| {
        mutate_vals(&tree, DEPTH, 50);
        update(&tree, 0, &mut vec);
    })
}

#[bench]
fn edit_50_seperate(b: &mut Bencher) {
    let mut vec = PresortedVec::new();
    let tree = build_tree(DEPTH, NODES);
    dump(&tree, &mut vec);
    b.iter(|| {
        for _ in 0..50 {
            mutate_vals(&tree, DEPTH, 1);
            update(&tree, 0, &mut vec);
        }
    })
}

#[bench]
fn incr_50_batch(b: &mut Bencher) {
    let mut vec = PresortedVec::new();
    let tree = build_tree(DEPTH, NODES);
    dump(&tree, &mut vec);
    b.iter(|| {
        incr_vals(&tree, DEPTH, 50);
        update(&tree, 0, &mut vec);
    })
}

#[bench]
fn incr_50_seperate(b: &mut Bencher) {
    let mut vec = PresortedVec::new();
    let tree = build_tree(DEPTH, NODES);
    dump(&tree, &mut vec);
    b.iter(|| {
        for _ in 0..50 {
            incr_vals(&tree, DEPTH, 1);
            update(&tree, 0, &mut vec);
        }
    })
}

#[bench]
fn add_50_batch(b: &mut Bencher) {
    let mut vec = PresortedVec::new();
    let tree = build_tree(DEPTH, NODES);
    dump(&tree, &mut vec);
    b.iter(|| {
        add_branches(&tree, DEPTH, 50);
        update(&tree, 0, &mut vec);
    })
}

#[bench]
fn add_50_seperate(b: &mut Bencher) {
    let mut vec = PresortedVec::new();
    let tree = build_tree(DEPTH, NODES);
    dump(&tree, &mut vec);
    b.iter(|| {
        for _ in 0..50 {
            add_branches(&tree, DEPTH, 50);
            update(&tree, 0, &mut vec);
        }
    })
}

<commit_msg>Benchmark sorting.<commit_after>#![feature(test)]

extern crate test;
extern crate rand;
extern crate presort;

use test::Bencher;
use rand::Rng;

use presort::PresortedVec;
use presort::inc_tree::{Tree, IncTree, dump, update};

const NODES: usize = 10000;
const BINARY_DEPTH: usize = 13;
const DEEP_DEPTH: usize = 150;
const WIDE_DEPTH: usize = 6;
const DEPTH: usize = BINARY_DEPTH;

//creates a random tree with the requested parameters
fn build_tree(max_depth: usize, nodes: usize) -> Tree<usize> {
    let mut rng = rand::thread_rng();

    let tree = Tree::new_node(rng.gen());

    for _ in 0..nodes {
        let mut branch = tree.clone();
        let depth = rng.gen::<usize>() % max_depth;
        for _ in 0..depth {
            let num = branch.num_children();
            if num <= 0 {break};
            branch = branch.get_child(rng.gen::<usize>() % num);
        }
        branch.push_child(Tree::new_node(rng.gen()));
    }

    tree
}

// Pick a random subtree from the tree
fn random_subtree(tree: &Tree<usize>, high_depth: usize) -> Tree<usize> {
    let mut rng = rand::thread_rng();
    let mut subtree = tree.clone();
    let depth = rng.gen::<usize>() % high_depth;
    for _ in 0..depth {
        let num = subtree.num_children();
        if num <= 0 { return subtree; }
        subtree = subtree.get_child(rng.gen::<usize>() % num);
    }
    return subtree;
}

// mutates `edits` number of values of `tree`,
// at no more than a max depth of `high_depth`
// shorter branches may have the ends updated more often
fn mutate_vals(tree: &Tree<usize>, high_depth: usize, edits: usize) {
    let mut rng = rand::thread_rng();
    for _ in 0..edits {
        random_subtree(tree, high_depth).set_data(rng.gen());
    }
}

fn incr_vals(tree: &Tree<usize>, high_depth: usize, edits: usize) {
    for _ in 0..edits {
        let subtree = random_subtree(tree, high_depth);
        subtree.set_data(subtree.get_data() + 1);
    }
}

fn add_branches(tree: &Tree<usize>, high_depth: usize, adds: usize) {
    let mut rng = rand::thread_rng();
    for _ in 0..adds {
        random_subtree(tree, high_depth).push_child(Tree::new_node(rng.gen()));
    }
}

#[bench]
fn edit_50_batch(b: &mut Bencher) {
    let mut vec = PresortedVec::new();
    let tree = build_tree(DEPTH, NODES);
    dump(&tree, &mut vec);
    b.iter(|| {
        mutate_vals(&tree, DEPTH, 50);
        update(&tree, 0, &mut vec);
        vec.sort();
    })
}

#[bench]
fn edit_50_seperate(b: &mut Bencher) {
    let mut vec = PresortedVec::new();
    let tree = build_tree(DEPTH, NODES);
    dump(&tree, &mut vec);
    b.iter(|| {
        for _ in 0..50 {
            mutate_vals(&tree, DEPTH, 1);
            update(&tree, 0, &mut vec);
            vec.sort();
        }
    })
}

#[bench]
fn incr_50_batch(b: &mut Bencher) {
    let mut vec = PresortedVec::new();
    let tree = build_tree(DEPTH, NODES);
    dump(&tree, &mut vec);
    b.iter(|| {
        incr_vals(&tree, DEPTH, 50);
        update(&tree, 0, &mut vec);
        vec.sort();
    })
}

#[bench]
fn incr_50_seperate(b: &mut Bencher) {
    let mut vec = PresortedVec::new();
    let tree = build_tree(DEPTH, NODES);
    dump(&tree, &mut vec);
    b.iter(|| {
        for _ in 0..50 {
            incr_vals(&tree, DEPTH, 1);
            update(&tree, 0, &mut vec);
            vec.sort();
        }
    })
}

#[bench]
fn add_50_batch(b: &mut Bencher) {
    let mut vec = PresortedVec::new();
    let tree = build_tree(DEPTH, NODES);
    dump(&tree, &mut vec);
    b.iter(|| {
        add_branches(&tree, DEPTH, 50);
        update(&tree, 0, &mut vec);
        vec.sort();
    })
}

#[bench]
fn add_50_seperate(b: &mut Bencher) {
    let mut vec = PresortedVec::new();
    let tree = build_tree(DEPTH, NODES);
    dump(&tree, &mut vec);
    b.iter(|| {
        for _ in 0..50 {
            add_branches(&tree, DEPTH, 50);
            update(&tree, 0, &mut vec);
            vec.sort();
        }
    })
}

<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::dynamic_lib::DynamicLibrary;
use semver::Version;

use core::{PackageId, Package};
use util::{mod, CargoResult};

/// A structure returning the result of a compilation.
pub struct Compilation {
    /// All libraries which were built for a package.
    ///
    /// This is currently used for passing --extern flags to rustdoc tests later
    /// on.
    pub libraries: HashMap<PackageId, Vec<Path>>,

    /// An array of all tests created during this compilation.
    pub tests: Vec<(String, Path)>,

    /// An array of all binaries created.
    pub binaries: Vec<Path>,

    /// All directires for the output of native build commands.
    ///
    /// This is currently used to drive some entries which are added to the
    /// LD_LIBRARY_PATH as appropriate.
    // TODO: deprecated, remove
    pub native_dirs: HashMap<PackageId, Path>,

    /// Root output directory (for the local package's artifacts)
    pub root_output: Path,

    /// Output directory for rust dependencies
    pub deps_output: Path,

    /// Extra environment variables that were passed to compilations and should
    /// be passed to future invocations of programs.
    pub extra_env: HashMap<String, Option<String>>,

    /// Top-level package that was compiled
    pub package: Package,
}

impl Compilation {
    pub fn new(pkg: &Package) -> Compilation {
        Compilation {
            libraries: HashMap::new(),
            native_dirs: HashMap::new(),  // TODO: deprecated, remove
            root_output: Path::new("/"),
            deps_output: Path::new("/"),
            tests: Vec::new(),
            binaries: Vec::new(),
            extra_env: HashMap::new(),
            package: pkg.clone(),
        }
    }

    /// Prepares a new process with an appropriate environment to run against
    /// the artifacts produced by the build process.
    ///
    /// The package argument is also used to configure environment variables as
    /// well as the working directory of the child process.
    pub fn process<T: ToCStr>(&self, cmd: T, pkg: &Package)
                              -> CargoResult<util::ProcessBuilder> {
        let mut search_path = DynamicLibrary::search_path();
        for dir in self.native_dirs.values() {
            search_path.push(dir.clone());
        }
        search_path.push(self.root_output.clone());
        search_path.push(self.deps_output.clone());
        let search_path = try!(util::join_paths(search_path.as_slice(),
                                                DynamicLibrary::envvar()));
        let mut cmd = try!(util::process(cmd)).env(
            DynamicLibrary::envvar(), Some(search_path.as_slice()));
        for (k, v) in self.extra_env.iter() {
            cmd = cmd.env(k.as_slice(), v.as_ref().map(|s| s.as_slice()));
        }

        Ok(cmd.env("CARGO_MANIFEST_DIR", Some(pkg.get_manifest_path().dir_path()))
              .env("CARGO_PKG_VERSION_MAJOR",
                   Some(pkg.get_version().major.to_string()))
              .env("CARGO_PKG_VERSION_MINOR",
                  Some(pkg.get_version().minor.to_string()))
              .env("CARGO_PKG_VERSION_PATCH",
                   Some(pkg.get_version().patch.to_string()))
              .env("CARGO_PKG_VERSION_PRE",
                   pre_version_component(pkg.get_version()))
              .cwd(pkg.get_root()))
    }
}

fn pre_version_component(v: &Version) -> Option<String> {
    if v.pre.is_empty() {
        return None;
    }

    let mut ret = String::new();

    for (i, x) in v.pre.iter().enumerate() {
        if i != 0 { ret.push('.') };
        ret.push_str(x.to_string().as_slice());
    }

    Some(ret)
}
<commit_msg>Export full package version as CARGO_PKG_VERSION<commit_after>use std::collections::HashMap;
use std::dynamic_lib::DynamicLibrary;
use semver::Version;

use core::{PackageId, Package};
use util::{mod, CargoResult};

/// A structure returning the result of a compilation.
pub struct Compilation {
    /// All libraries which were built for a package.
    ///
    /// This is currently used for passing --extern flags to rustdoc tests later
    /// on.
    pub libraries: HashMap<PackageId, Vec<Path>>,

    /// An array of all tests created during this compilation.
    pub tests: Vec<(String, Path)>,

    /// An array of all binaries created.
    pub binaries: Vec<Path>,

    /// All directires for the output of native build commands.
    ///
    /// This is currently used to drive some entries which are added to the
    /// LD_LIBRARY_PATH as appropriate.
    // TODO: deprecated, remove
    pub native_dirs: HashMap<PackageId, Path>,

    /// Root output directory (for the local package's artifacts)
    pub root_output: Path,

    /// Output directory for rust dependencies
    pub deps_output: Path,

    /// Extra environment variables that were passed to compilations and should
    /// be passed to future invocations of programs.
    pub extra_env: HashMap<String, Option<String>>,

    /// Top-level package that was compiled
    pub package: Package,
}

impl Compilation {
    pub fn new(pkg: &Package) -> Compilation {
        Compilation {
            libraries: HashMap::new(),
            native_dirs: HashMap::new(),  // TODO: deprecated, remove
            root_output: Path::new("/"),
            deps_output: Path::new("/"),
            tests: Vec::new(),
            binaries: Vec::new(),
            extra_env: HashMap::new(),
            package: pkg.clone(),
        }
    }

    /// Prepares a new process with an appropriate environment to run against
    /// the artifacts produced by the build process.
    ///
    /// The package argument is also used to configure environment variables as
    /// well as the working directory of the child process.
    pub fn process<T: ToCStr>(&self, cmd: T, pkg: &Package)
                              -> CargoResult<util::ProcessBuilder> {
        let mut search_path = DynamicLibrary::search_path();
        for dir in self.native_dirs.values() {
            search_path.push(dir.clone());
        }
        search_path.push(self.root_output.clone());
        search_path.push(self.deps_output.clone());
        let search_path = try!(util::join_paths(search_path.as_slice(),
                                                DynamicLibrary::envvar()));
        let mut cmd = try!(util::process(cmd)).env(
            DynamicLibrary::envvar(), Some(search_path.as_slice()));
        for (k, v) in self.extra_env.iter() {
            cmd = cmd.env(k.as_slice(), v.as_ref().map(|s| s.as_slice()));
        }

        Ok(cmd.env("CARGO_MANIFEST_DIR", Some(pkg.get_manifest_path().dir_path()))
              .env("CARGO_PKG_VERSION_MAJOR",
                   Some(pkg.get_version().major.to_string()))
              .env("CARGO_PKG_VERSION_MINOR",
                  Some(pkg.get_version().minor.to_string()))
              .env("CARGO_PKG_VERSION_PATCH",
                   Some(pkg.get_version().patch.to_string()))
              .env("CARGO_PKG_VERSION_PRE",
                   pre_version_component(pkg.get_version()))
              .env("CARGO_PKG_VERSION",
                   Some(pkg.get_version().to_string()))
              .cwd(pkg.get_root()))
    }
}

fn pre_version_component(v: &Version) -> Option<String> {
    if v.pre.is_empty() {
        return None;
    }

    let mut ret = String::new();

    for (i, x) in v.pre.iter().enumerate() {
        if i != 0 { ret.push('.') };
        ret.push_str(x.to_string().as_slice());
    }

    Some(ret)
}
<|endoftext|>