<commit_before>#![crate_name="tag_safe"]
#![crate_type="dylib"]
#![feature(plugin_registrar, rustc_private, box_syntax)]

#[macro_use]
extern crate log;

extern crate syntax;
#[macro_use]
extern crate rustc;

use syntax::ast;
use syntax::visit;
use syntax::codemap::Span;
use rustc::lint::LintPassObject;
use rustc::plugin::Registry;
use rustc::lint::{Context, LintPass, LintArray};
use syntax::attr::AttrMetaMethods;
use rustc::middle::{def,ty};

declare_lint!(TAGGED_SAFE_LINT, Warn, "Warn about use of non-tagged methods within tagged function");

struct Pass;

struct Visitor<'a, 'tcx: 'a>
{
    lcx: &'a Context<'a, 'tcx>,
	name: &'a str,
}

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(TAGGED_SAFE_LINT)
    }

    fn check_fn(&mut self, cx: &Context, _: visit::FnKind, decl: &ast::FnDecl, body: &ast::Block, _: Span, id: ast::NodeId) {
        let attrs = cx.tcx.map.attrs(id);
        for ty in attrs.iter()
            .filter(|a| a.check_name("tag_safe"))
            .filter_map(|a| a.meta_item_list())
            .flat_map(|x| x.iter())
        {
            // Search body for calls to non safe methods
            let mut v = Visitor{ lcx: cx, name: &ty.name() };
            debug!("Method {:?} is marked safe '{}'", id, ty.name());
            visit::walk_block(&mut v, body);
        }
    }
}

/// Locate a #[tag_safe(<name>)] attribute on the passed item
fn method_is_safe(tcx: &ty::ctxt, id: ast::DefId, name: &str) -> bool
{
	if id.krate == 1
	{
		tcx.map.attrs(id.node).iter().any( |a|
			a.check_name("tag_safe")
			&& a.meta_item_list().iter().flat_map(|x| x.iter()).any(|a| a.name() == name)
			)
	}
	else
	{
		error!("TODO: Crate ID non-zero {:?}", id);
		false
	}
}

impl<'a, 'tcx: 'a> visit::Visitor<'a> for Visitor<'a,'tcx>
{
    // - visit_expr_post doesn't need to do anything
    fn visit_expr_post(&mut self, ex: &'a ast::Expr) {
        match ex.node
        {
        ast::ExprCall(ref fcn, _) =>
            match fcn.node
            {
            ast::ExprPath(ref qs, ref p) => {
                    if let def::DefFn(did, _) = ty::resolve_expr(self.lcx.tcx, &fcn) {
						let is_safe = method_is_safe(self.lcx.tcx, did, self.name);
						if !is_safe
						{
							self.lcx.span_lint(TAGGED_SAFE_LINT, ex.span,
								&format!("Calling untagged method from a #[tag_safe({})] method", self.name)[..]
								);
						}
                    }
                },
            _ => {},
            },
        ast::ExprMethodCall(ref id, ref tys, ref exprs) => {
					let obj_node_id = exprs[0].id;
                    debug!("Call method {:?} {:?}", id, obj_node_id);
					let mm = self.lcx.tcx.method_map.borrow();
					match mm.get( &ty::MethodCall::expr(ex.id) ).unwrap().origin
					{
					ty::MethodStatic(id) => {
							if !method_is_safe(self.lcx.tcx, id, self.name) {
								self.lcx.span_lint(TAGGED_SAFE_LINT, ex.span,
									&format!("Calling untagged method from a #[tag_safe({})] method", self.name)[..]
									);
							}
						},
					_ => {},
					}
                },
        _ => {},
        }
    }
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_lint_pass(box Pass as LintPassObject);
}

<commit_msg>SynExts/tag_safe - Extend to detect tag_unsafe<commit_after>// tag_safe
//
// A linting plugin to flag calls to methods not marked "tag_safe"
// from methods marked "tag_safe".
//
// Author: John Hodge (thePowersGang/Mutabah)
//
// TODO: Support '#[tag_unsafe(type)]' which is used when a method has no marker
// - Allows default safe fallback, with upwards propagation.
//
#![crate_name="tag_safe"]
#![crate_type="dylib"]
#![feature(plugin_registrar, rustc_private, box_syntax)]

#[macro_use]
extern crate log;

extern crate syntax;
#[macro_use]
extern crate rustc;

use syntax::ast;
use syntax::visit;
use syntax::codemap::Span;
use rustc::lint::LintPassObject;
use rustc::plugin::Registry;
use rustc::lint::{Context, LintPass, LintArray};
use syntax::attr::AttrMetaMethods;
use rustc::middle::{def,ty};

declare_lint!(NOT_TAGGED_SAFE, Warn, "Warn about use of non-tagged methods within tagged function");

#[derive(Copy,Clone,Debug)]
enum SafetyType
{
	Safe,
	Unsafe,
	Unknown,
}

#[derive(Default)]
struct Pass
{
	/// Cache of flag types
	flag_types: Vec<String>,
	/// Node => (Type => IsSafe)
	flag_cache: ::rustc::util::nodemap::NodeMap< ::rustc::util::nodemap::FnvHashMap<usize, SafetyType> >,

	lvl: usize,
}

struct Visitor<'a, 'tcx: 'a, F: FnMut(&Span) + 'a>
{
	pass: &'a mut Pass,
    tcx: &'a ty::ctxt<'tcx>,
	name: &'a str,
	unknown_assume: bool,
	cb: F,
}

struct Indent(usize);
impl ::std::fmt::Display for Indent {
	fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
		for s in ::std::iter::repeat(" ").take(self.0) {
			try!(write!(f, "{}", s));
		}
		Ok( () )
	}
}

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(NOT_TAGGED_SAFE)
    }

    fn check_fn(&mut self, cx: &Context, _kind: visit::FnKind, _decl: &ast::FnDecl, body: &ast::Block, _: Span, id: ast::NodeId) {
        let attrs = cx.tcx.map.attrs(id);
        for ty in attrs.iter()
            .filter(|a| a.check_name("tag_safe"))
            .filter_map(|a| a.meta_item_list())
            .flat_map(|x| x.iter())
        {
            // Search body for calls to non safe methods
            let mut v = Visitor{
					pass: self, tcx: cx.tcx, name: &ty.name(),
					// - Assumes an untagged method is unsafe
					unknown_assume: false,
					cb: |span| {
							cx.span_lint(NOT_TAGGED_SAFE, *span,
								&format!("Calling {}-unsafe method from a #[tag_safe({})] method", ty.name(), ty.name())[..]
								);
						},
					};
            debug!("Method {:?} is marked safe '{}'", id, ty.name());
            visit::walk_block(&mut v, body);
        }
    }
}

impl Pass
{
	fn check_for_marker(tcx: &ty::ctxt, id: ast::NodeId, marker: &str, name: &str) -> bool
	{
		debug!("Checking for marker {}({}) on {:?}", marker, name, id);
		tcx.map.attrs(id).iter()
			.filter_map( |a| if a.check_name(marker) { a.meta_item_list() } else { None })
			.flat_map(|x| x.iter())
			.any(|a| a.name() == name)
	}

	fn crate_method_is_safe(&mut self, tcx: &ty::ctxt, node_id: ast::NodeId, name: &str, unknown_assume: bool) -> bool
	{
		// Obtain tag name ID (avoids storing a string in the map)
		let name_id =
			match self.flag_types.iter().position(|a| *a == name)
			{
			Some(v) => v,
			None => {
				self.flag_types.push( String::from(name) );
				self.flag_types.len() - 1
				},
			};

		// Check cache first
		if let Some(&st) = self.flag_cache.get(&node_id).and_then(|a| a.get(&name_id))
		{
			match st
			{
			SafetyType::Safe => true,
			SafetyType::Unsafe => false,
			SafetyType::Unknown => unknown_assume,
			}
		}
		else
		{
			// Search for a safety marker, possibly recursing
			let is_safe =
				if Self::check_for_marker(tcx, node_id, "tag_safe", name) {
					true
				}
				else if Self::check_for_marker(tcx, node_id, "tag_unsafe", name) {
					false
				}
				else {
					// Cache this method as unknown (to prevent infinite recursion)
					self.flag_cache.entry(node_id)
						.or_insert(Default::default())
						.insert(name_id, SafetyType::Unknown)
						;

					match tcx.map.get(node_id)
					{
					syntax::ast_map::NodeItem(i) =>
						match i.node {
						ast::ItemFn(_, _, _, _, ref body) => {
							// Enumerate this function's code, recursively checking for a call to an unsafe method
							let mut is_safe = true;
							{
								let mut v = Visitor {
									pass: self, tcx: tcx, name: name,
									unknown_assume: true,
									cb: |_| { is_safe = false; }
									};
								visit::walk_block(&mut v, body);
							}
							is_safe
							},
						_ => unknown_assume,
						},
					syntax::ast_map::NodeImplItem(i) =>
						match i.node {
						ast::MethodImplItem(_, ref body) => {
							let mut is_safe = true;
							{
								let mut v = Visitor {
									pass: self, tcx: tcx, name: name,
									unknown_assume: true,
									cb: |_| { is_safe = false; }
									};
								visit::walk_block(&mut v, body);
							}
							is_safe
							},
						_ => unknown_assume,
						},
					v @ _ => {
						error!("Node ID {} points to non-item {:?}", node_id, v);
						unknown_assume
						}
					}
				};
			self.flag_cache.entry(node_id)
				.or_insert(Default::default())
				.insert(name_id, if is_safe { SafetyType::Safe } else { SafetyType::Unsafe })
				;
			is_safe
		}
	}

	/// Locate a #[tag_safe(<name>)] attribute on the passed item
	pub fn method_is_safe(&mut self, tcx: &ty::ctxt, id: ast::DefId, name: &str, unknown_assume: bool) -> bool
	{
		self.lvl += 1;
		debug!("{}Checking method {:?} (A {})", Indent(self.lvl), id, unknown_assume);
		let rv = if id.krate == 0
			{
				// Dummy assumption value, as none should be unknown until recursion starts
				self.crate_method_is_safe(tcx, id.node, name, unknown_assume)
			}
			else
			{
				error!("TODO: Crate ID non-zero {:?} (assuming safe)", id);
				true
			};
		debug!("{}Checking method {:?} = {}", Indent(self.lvl), id, rv);
		self.lvl -= 1;
		rv
	}
}

impl<'a, 'tcx: 'a, F: FnMut(&Span)> visit::Visitor<'a> for Visitor<'a,'tcx, F>
{
    // - visit_expr_post doesn't need to do anything
    fn visit_expr_post(&mut self, ex: &'a ast::Expr) {
        match ex.node
        {
        ast::ExprCall(ref fcn, _) =>
            match fcn.node
            {
            ast::ExprPath(ref _qs, ref _p) => {
                    if let def::DefFn(did, _) = ty::resolve_expr(self.tcx, &fcn) {
						if !self.pass.method_is_safe(self.tcx, did, self.name, self.unknown_assume)
						{
							(self.cb)(&ex.span);
						}
                    }
                },
            _ => {},
            },
        ast::ExprMethodCall(ref id, ref _tys, ref exprs) => {
					let obj_node_id = exprs[0].id;
                    debug!("Call method {:?} {:?}", id.node, obj_node_id);
					let mm = self.tcx.method_map.borrow();
					match mm.get( &ty::MethodCall::expr(ex.id) ).unwrap().origin
					{
					ty::MethodStatic(id) => {
							if !self.pass.method_is_safe(self.tcx, id, self.name, self.unknown_assume) {
								(self.cb)(&ex.span);
							}
						},
					_ => {},
					}
                },
        _ => {},
        }
    }
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_lint_pass(box Pass::default() as LintPassObject);
}

<|endoftext|>


<commit_before>/// Based on the bunnymark example from [`tetra`](https://crates.io/crates/tetra)
/// which is based on https://github.com/openfl/openfl-samples/tree/master/demos/BunnyMark
/// Original BunnyMark (and sprite) by Iain Lobb
use std::env;
use std::path;

use rand::rngs::ThreadRng;
use rand::{self, Rng};
use nalgebra as na;

use ggez::*;
use ggez::graphics::{Color, Image};
use ggez::Context;

// NOTE: Using a high number here yields worse performance than adding more bunnies over
// time - I think this is due to all of the RNG being run on the same tick...
const INITIAL_BUNNIES: usize = 100;
const WIDTH: u16 = 1280;
const HEIGHT: u16 = 720;
const GRAVITY: f32 = 0.5;

struct Bunny {
    position: na::Point2<f32>,
    velocity: na::Vector2<f32>,
}

impl Bunny {
    fn new(rng: &mut ThreadRng) -> Bunny {
        let x_vel = rng.gen::<f32>() * 5.0;
        let y_vel = (rng.gen::<f32>() * 5.0) - 2.5;

        Bunny {
            position: na::Point2::new(0.0, 0.0),
            velocity: na::Vector2::new(x_vel, y_vel),
        }
    }
}

struct GameState {
    rng: ThreadRng,
    texture: Image,
    bunnies: Vec<Bunny>,
    max_x: f32,
    max_y: f32,

    click_timer: i32,
}

impl GameState {
    fn new(ctx: &mut Context) -> ggez::GameResult<GameState> {
        let mut rng = rand::thread_rng();
        let texture = Image::new(ctx, "/resources/wabbit_alpha.png")?;
        let mut bunnies = Vec::with_capacity(INITIAL_BUNNIES);
        let max_x = (WIDTH - texture.width()) as f32;
        let max_y = (HEIGHT - texture.height()) as f32;

        for _ in 0..INITIAL_BUNNIES {
            bunnies.push(Bunny::new(&mut rng));
        }

        Ok(GameState {
            rng,
            texture,
            bunnies,
            max_x,
            max_y,

            click_timer: 0,
        })
    }
}

impl event::EventHandler for GameState {
    fn update(&mut self, _ctx: &mut Context) -> GameResult {
        if self.click_timer > 0 {
            self.click_timer -= 1;
        }

        for bunny in &mut self.bunnies {
            bunny.position += bunny.velocity;
            bunny.velocity.y += GRAVITY;

            if bunny.position.x > self.max_x {
                bunny.velocity.x *= -1.0;
                bunny.position.x = self.max_x;
            } else if bunny.position.x < 0.0 {
                bunny.velocity.x *= -1.0;
                bunny.position.x = 0.0;
            }

            if bunny.position.y > self.max_y {
                bunny.velocity.y *= -0.8;
                bunny.position.y = self.max_y;

                if self.rng.gen::<bool>() {
                    bunny.velocity.y -= 3.0 + (self.rng.gen::<f32>() * 4.0);
                }
            } else if bunny.position.y < 0.0 {
                bunny.velocity.y = 0.0;
                bunny.position.y = 0.0;
            }
        }

        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> GameResult {
        graphics::clear(ctx, Color::from((0.392, 0.584, 0.929)));

        for bunny in &self.bunnies {
            graphics::draw(ctx, &self.texture, (bunny.position, ))?;
        }

        graphics::set_window_title(
            ctx,
            &format!(
                "BunnyMark - {} bunnies - {:.0} FPS",
                self.bunnies.len(),
                timer::fps(ctx)
            ),
        );

        Ok(())
    }

    fn mouse_button_down_event(&mut self, _ctx: &mut Context,
                               button: input::mouse::MouseButton,
                               _x: f32,
                               _y: f32) {
        if button == input::mouse::MouseButton::Left && self.click_timer == 0 {
            for _ in 0..INITIAL_BUNNIES {
                self.bunnies.push(Bunny::new(&mut self.rng));
            }
            self.click_timer = 10;
        }
    }

}

fn main() -> GameResult {
    let resource_dir = if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
        let mut path = path::PathBuf::from(manifest_dir);
        path.push("resources");
        path
    } else {
        path::PathBuf::from("./resources")
    };

    let cb = ggez::ContextBuilder::new("bunnymark", "ggez").add_resource_path(resource_dir);
    let (ctx, event_loop) = &mut cb.build()?;

    let state = &mut GameState::new(ctx)?;
    event::run(ctx, event_loop, state)

    /*

    let cb = ggez::ContextBuilder::new("bunnymark", "ggez");
    let (ctx, event_loop) = &mut cb.build()?;
    let state = &mut MainState::new(ctx)?;
    event::run(ctx, event_loop, state)

    ContextBuilder::new("BunnyMark", WIDTH, HEIGHT)
        .quit_on_escape(true)
        .build()?
        .run_with(GameState::new)
*/
}
<commit_msg>Minor fixes<commit_after>/// Based on the bunnymark example from [`tetra`](https://crates.io/crates/tetra)
/// which is based on https://github.com/openfl/openfl-samples/tree/master/demos/BunnyMark
/// Original BunnyMark (and sprite) by Iain Lobb
use std::env;
use std::path;

use rand::rngs::ThreadRng;
use rand::{self, Rng};
use nalgebra as na;

use ggez::*;
use ggez::graphics::{Color, Image};
use ggez::Context;

// NOTE: Using a high number here yields worse performance than adding more bunnies over
// time - I think this is due to all of the RNG being run on the same tick...
const INITIAL_BUNNIES: usize = 100;
const WIDTH: u16 = 1280;
const HEIGHT: u16 = 720;
const GRAVITY: f32 = 0.5;

struct Bunny {
    position: na::Point2<f32>,
    velocity: na::Vector2<f32>,
}

impl Bunny {
    fn new(rng: &mut ThreadRng) -> Bunny {
        let x_vel = rng.gen::<f32>() * 5.0;
        let y_vel = (rng.gen::<f32>() * 5.0) - 2.5;

        Bunny {
            position: na::Point2::new(0.0, 0.0),
            velocity: na::Vector2::new(x_vel, y_vel),
        }
    }
}

struct GameState {
    rng: ThreadRng,
    texture: Image,
    bunnies: Vec<Bunny>,
    max_x: f32,
    max_y: f32,

    click_timer: i32,
}

impl GameState {
    fn new(ctx: &mut Context) -> ggez::GameResult<GameState> {
        let mut rng = rand::thread_rng();
        let texture = Image::new(ctx, "/wabbit_alpha.png")?;
        let mut bunnies = Vec::with_capacity(INITIAL_BUNNIES);
        let max_x = (WIDTH - texture.width()) as f32;
        let max_y = (HEIGHT - texture.height()) as f32;

        for _ in 0..INITIAL_BUNNIES {
            bunnies.push(Bunny::new(&mut rng));
        }

        Ok(GameState {
            rng,
            texture,
            bunnies,
            max_x,
            max_y,

            click_timer: 0,
        })
    }
}

impl event::EventHandler for GameState {
    fn update(&mut self, _ctx: &mut Context) -> GameResult {
        if self.click_timer > 0 {
            self.click_timer -= 1;
        }

        for bunny in &mut self.bunnies {
            bunny.position += bunny.velocity;
            bunny.velocity.y += GRAVITY;

            if bunny.position.x > self.max_x {
                bunny.velocity.x *= -1.0;
                bunny.position.x = self.max_x;
            } else if bunny.position.x < 0.0 {
                bunny.velocity.x *= -1.0;
                bunny.position.x = 0.0;
            }

            if bunny.position.y > self.max_y {
                bunny.velocity.y *= -0.8;
                bunny.position.y = self.max_y;

                if self.rng.gen::<bool>() {
                    bunny.velocity.y -= 3.0 + (self.rng.gen::<f32>() * 4.0);
                }
            } else if bunny.position.y < 0.0 {
                bunny.velocity.y = 0.0;
                bunny.position.y = 0.0;
            }
        }

        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> GameResult {
        graphics::clear(ctx, Color::from((0.392, 0.584, 0.929)));

        for bunny in &self.bunnies {
            graphics::draw(ctx, &self.texture, (bunny.position, ))?;
        }

        graphics::set_window_title(
            ctx,
            &format!(
                "BunnyMark - {} bunnies - {:.0} FPS",
                self.bunnies.len(),
                timer::fps(ctx)
            ),
        );
        graphics::present(ctx)?;

        Ok(())
    }

    fn mouse_button_down_event(&mut self, _ctx: &mut Context,
                               button: input::mouse::MouseButton,
                               _x: f32,
                               _y: f32) {
        if button == input::mouse::MouseButton::Left && self.click_timer == 0 {
            for _ in 0..INITIAL_BUNNIES {
                self.bunnies.push(Bunny::new(&mut self.rng));
            }
            self.click_timer = 10;
        }
    }

}

fn main() -> GameResult {
    let resource_dir = if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
        let mut path = path::PathBuf::from(manifest_dir);
        path.push("resources");
        path
    } else {
        path::PathBuf::from("./resources")
    };

    let cb = ggez::ContextBuilder::new("bunnymark", "ggez").add_resource_path(resource_dir);
    let (ctx, event_loop) = &mut cb.build()?;

    let state = &mut GameState::new(ctx)?;
    event::run(ctx, event_loop, state)

    /*

    let cb = ggez::ContextBuilder::new("bunnymark", "ggez");
    let (ctx, event_loop) = &mut cb.build()?;
    let state = &mut MainState::new(ctx)?;
    event::run(ctx, event_loop, state)

    ContextBuilder::new("BunnyMark", WIDTH, HEIGHT)
        .quit_on_escape(true)
        .build()?
        .run_with(GameState::new)
*/
}
<|endoftext|>


<commit_before>extern crate gimli;

use std::cell::Cell;
use std::env;

fn main() {
    for file_path in env::args().skip(1) {
        println!("{}", file_path);
        println!("");

        let file = obj::open(&file_path);
        if obj::is_little_endian(&file) {
            dump_file::<gimli::LittleEndian>(file);
        } else {
            dump_file::<gimli::BigEndian>(file);
        }
    }
}

fn dump_file<Endian>(file: obj::File)
    where Endian: gimli::Endianity
{
    let debug_abbrev = obj::get_section(&file, ".debug_abbrev")
        .expect("Does not have .debug_abbrev section");
    let debug_abbrev = gimli::DebugAbbrev::<Endian>::new(debug_abbrev);

    dump_info(&file, debug_abbrev);
    dump_types(&file, debug_abbrev);
    dump_line(&file, debug_abbrev);
}

fn dump_info<Endian>(file: &obj::File, debug_abbrev: gimli::DebugAbbrev<Endian>)
    where Endian: gimli::Endianity
{
    if let Some(debug_info) = obj::get_section(file, ".debug_info") {
        println!(".debug_info");
        println!("");

        let debug_info = gimli::DebugInfo::<Endian>::new(&debug_info);

        for unit in debug_info.units() {
            let unit = unit.expect("Should parse the unit OK");

            let abbrevs = unit.abbreviations(debug_abbrev)
                .expect("Error parsing abbreviations");

            dump_entries(unit.entries(&abbrevs));
        }
    }
}

fn dump_types<Endian>(file: &obj::File, debug_abbrev: gimli::DebugAbbrev<Endian>)
    where Endian: gimli::Endianity
{
    if let Some(debug_types) = obj::get_section(file, ".debug_types") {
        println!(".debug_types");
        println!("");

        let debug_types = gimli::DebugTypes::<Endian>::new(&debug_types);

        for unit in debug_types.units() {
            let unit = unit.expect("Should parse the unit OK");

            let abbrevs = unit.abbreviations(debug_abbrev)
                .expect("Error parsing abbreviations");

            dump_entries(unit.entries(&abbrevs));
        }
    }
}

fn dump_entries<Endian>(mut entries: gimli::EntriesCursor<Endian>)
    where Endian: gimli::Endianity
{
    let depth = Cell::new(0);
    while let Some((delta_depth, entry)) = entries.next_dfs().expect("Should parse next dfs") {
        depth.set(depth.get() + delta_depth);
        let indent = || {
            for _ in 0..(depth.get() as usize) {
                print!("        ");
            }
        };

        indent();
        println!("<{}> <{}>", entry.code(), entry.tag());

        let mut attrs = entry.attrs();
        while let Some(attr) = attrs.next().expect("Should parse attribute OK") {
            indent();
            println!("    {} = {:?}", attr.name(), attr.value());
        }
    }
}

fn dump_line<Endian>(file: &obj::File, debug_abbrev: gimli::DebugAbbrev<Endian>)
    where Endian: gimli::Endianity
{
    let debug_line = obj::get_section(file, ".debug_line");
    let debug_info = obj::get_section(file, ".debug_info");

    if let (Some(debug_line), Some(debug_info)) = (debug_line, debug_info) {
        println!(".debug_line");
        println!("");

        let debug_line = gimli::DebugLine::<Endian>::new(&debug_line);
        let debug_info = gimli::DebugInfo::<Endian>::new(&debug_info);

        for unit in debug_info.units() {
            let unit = unit.expect("Should parse unit header OK");

            let abbrevs = unit.abbreviations(debug_abbrev)
                .expect("Error parsing abbreviations");

            let mut cursor = unit.entries(&abbrevs);
            cursor.next_dfs().expect("Should parse next dfs");

            let root = cursor.current().expect("Should have a root DIE");
            let value = root.attr_value(gimli::DW_AT_stmt_list);
            let offset = gimli::DebugLineOffset(match value {
                Some(gimli::AttributeValue::Data(data)) if data.len() == 4 => {
                    Endian::read_u32(data) as u64
                }
                Some(gimli::AttributeValue::Data(data)) if data.len() == 8 => {
                    Endian::read_u64(data)
                }
                Some(gimli::AttributeValue::SecOffset(offset)) => offset,
                _ => continue,
            });

            let header =
                gimli::LineNumberProgramHeader::new(debug_line, offset, unit.address_size());
            if let Ok(header) = header {
                println!("");
                println!("Offset:                             0x{:x}", offset.0);
                println!("Length:                             {}",
                         header.unit_length());
                println!("DWARF version:                      {}", header.version());
                println!("Prologue length:                    {}",
                         header.header_length());
                println!("Minimum instruction length:         {}",
                         header.minimum_instruction_length());
                println!("Maximum operations per instruction: {}",
                         header.maximum_operations_per_instruction());
                println!("Default is_stmt:                    {}",
                         header.default_is_stmt());
                println!("Line base:                          {}", header.line_base());
                println!("Line range:                         {}",
                         header.line_range());
                println!("Opcode base:                        {}",
                         header.opcode_base());

                println!("");
                println!("Opcodes:");
                for (i, length) in header.standard_opcode_lengths().iter().enumerate() {
                    println!("  Opcode {} as {} args", i + 1, length);
                }

                println!("");
                println!("The Directory Table:");
                for (i, dir) in header.include_directories().iter().enumerate() {
                    println!("  {} {}", i + 1, dir.to_string_lossy());
                }

                println!("");
                println!("The File Name Table");
                println!("  Entry\tDir\tTime\tSize\tName");
                for (i, file) in header.file_names().iter().enumerate() {
                    println!("  {}\t{}\t{}\t{}\t{}",
                             i + 1,
                             file.directory_index(),
                             file.last_modification(),
                             file.length(),
                             file.path_name().to_string_lossy());
                }

                println!("");
                println!("Line Number Statements:");
                let mut opcodes = header.opcodes();
                while let Some(opcode) = opcodes.next_opcode().expect("Should parse opcode OK") {
                    println!("  {}", opcode);
                }
            }
        }
    }
}

// All cross platform / object file format compatibility stuff should
// be contained in the `obj` module. Each supported platform / object
// file format should implement the `obj` module with an identical
// interface, but with the `pub type File` changing as needed. Hooray
// duck typing!

#[cfg(target_os="linux")]
mod obj {
    extern crate elf;
    use std::path::Path;

    /// The parsed object file type.
    pub type File = elf::File;

    /// Open and parse the object file at the given path.
    pub fn open<P>(path: P) -> File
        where P: AsRef<Path>
    {
        let path = path.as_ref();
        elf::File::open_path(path).expect("Could not open file")
    }

    /// Get the contents of the section named `section_name`, if such
    /// a section exists.
    pub fn get_section<'a>(file: &'a File, section_name: &str) -> Option<&'a [u8]> {
        file.sections
            .iter()
            .find(|s| s.shdr.name == section_name)
            .map(|s| &s.data[..])
    }

    /// Return true if the file is little endian, false if it is big endian.
    pub fn is_little_endian(file: &File) -> bool {
        match file.ehdr.data {
            elf::types::ELFDATA2LSB => true,
            elf::types::ELFDATA2MSB => false,
            otherwise => panic!("Unknown endianity: {}", otherwise),
        }
    }
}

#[cfg(target_os="macos")]
mod obj {
    extern crate mach_o;

    use std::ffi::CString;
    use std::fs;
    use std::io::Read;
    use std::mem;
    use std::path::Path;

    pub type File = Vec<u8>;

    pub fn open<P>(path: P) -> File
        where P: AsRef<Path>
    {
        let mut file = fs::File::open(path).expect("Could not open file");
        let mut buf = Vec::new();
        file.read_to_end(&mut buf).expect("Could not read file");
        buf
    }

    // Translate the "." prefix to the "__" prefix used by OSX/Mach-O, eg
    // ".debug_info" to "__debug_info".
    fn translate_section_name(section_name: &str) -> CString {
        let mut name = Vec::with_capacity(section_name.len() + 1);
        name.push(b'_');
        name.push(b'_');
        for ch in &section_name.as_bytes()[1..] {
            name.push(*ch);
        }
        unsafe { CString::from_vec_unchecked(name) }
    }

    pub fn get_section<'a>(file: &'a File, section_name: &str) -> Option<&'a [u8]> {
        let parsed = mach_o::Header::new(&file[..]).expect("Could not parse macho-o file");

        let segment_name = CString::new("__DWARF").unwrap();
        let section_name = translate_section_name(section_name);
        parsed.get_section(&segment_name, &section_name).map(|s| s.data())
    }

    pub fn is_little_endian(file: &File) -> bool {
        let parsed = mach_o::Header::new(&file[..]).expect("Could not parse macho-o file");

        let bytes = [1, 0, 0, 0u8];
        let int: u32 = unsafe { mem::transmute(bytes) };
        let native_byteorder_is_little = int == 1;

        match (native_byteorder_is_little, parsed.is_native_byteorder()) {
            (true, b) => b,
            (false, b) => !b,
        }
    }
}
<commit_msg>Decode strings in dwarfdump.rs.<commit_after>extern crate gimli;

use std::cell::Cell;
use std::env;

fn main() {
    for file_path in env::args().skip(1) {
        println!("{}", file_path);
        println!("");

        let file = obj::open(&file_path);
        if obj::is_little_endian(&file) {
            dump_file::<gimli::LittleEndian>(file);
        } else {
            dump_file::<gimli::BigEndian>(file);
        }
    }
}

fn dump_file<Endian>(file: obj::File)
    where Endian: gimli::Endianity
{
    let debug_abbrev = obj::get_section(&file, ".debug_abbrev")
        .expect("Does not have .debug_abbrev section");
    let debug_abbrev = gimli::DebugAbbrev::<Endian>::new(debug_abbrev);
    let debug_str = obj::get_section(&file, ".debug_str")
        .expect("Does not have .debug_str section");
    let debug_str = gimli::DebugStr::<Endian>::new(debug_str);

    dump_info(&file, debug_abbrev, debug_str);
    dump_types(&file, debug_abbrev, debug_str);
    dump_line(&file, debug_abbrev);
}

fn dump_info<Endian>(file: &obj::File, debug_abbrev: gimli::DebugAbbrev<Endian>,
                     debug_str: gimli::DebugStr<Endian>)
    where Endian: gimli::Endianity
{
    if let Some(debug_info) = obj::get_section(file, ".debug_info") {
        println!(".debug_info");
        println!("");

        let debug_info = gimli::DebugInfo::<Endian>::new(&debug_info);

        for unit in debug_info.units() {
            let unit = unit.expect("Should parse the unit OK");

            let abbrevs = unit.abbreviations(debug_abbrev)
                .expect("Error parsing abbreviations");

            dump_entries(unit.entries(&abbrevs), debug_str);
        }
    }
}

fn dump_types<Endian>(file: &obj::File, debug_abbrev: gimli::DebugAbbrev<Endian>,
                      debug_str: gimli::DebugStr<Endian>)
    where Endian: gimli::Endianity
{
    if let Some(debug_types) = obj::get_section(file, ".debug_types") {
        println!(".debug_types");
        println!("");

        let debug_types = gimli::DebugTypes::<Endian>::new(&debug_types);

        for unit in debug_types.units() {
            let unit = unit.expect("Should parse the unit OK");

            let abbrevs = unit.abbreviations(debug_abbrev)
                .expect("Error parsing abbreviations");

            dump_entries(unit.entries(&abbrevs), debug_str);
        }
    }
}

fn dump_entries<Endian>(mut entries: gimli::EntriesCursor<Endian>, debug_str: gimli::DebugStr<Endian>)
    where Endian: gimli::Endianity
{
    let depth = Cell::new(0);
    while let Some((delta_depth, entry)) = entries.next_dfs().expect("Should parse next dfs") {
        depth.set(depth.get() + delta_depth);
        let indent = || {
            for _ in 0..(depth.get() as usize) {
                print!("        ");
            }
        };

        indent();
        println!("<{}> <{}>", entry.code(), entry.tag());

        let mut attrs = entry.attrs();
        while let Some(attr) = attrs.next().expect("Should parse attribute OK") {
            indent();
            let to_print: Option<gimli::AttributeValue> = match attr.value() {
                gimli::AttributeValue::DebugStrRef(o) => match debug_str.get_str(o) {
                    Ok(s) => Some(gimli::AttributeValue::String(s)),
                    Err(_) => None,
                },
                otherwise => Some(otherwise),
            };
            println!("    {} = {:?}", attr.name(), to_print.expect("Error parsing attribute value"));
        }
    }
}

fn dump_line<Endian>(file: &obj::File, debug_abbrev: gimli::DebugAbbrev<Endian>)
    where Endian: gimli::Endianity
{
    let debug_line = obj::get_section(file, ".debug_line");
    let debug_info = obj::get_section(file, ".debug_info");

    if let (Some(debug_line), Some(debug_info)) = (debug_line, debug_info) {
        println!(".debug_line");
        println!("");

        let debug_line = gimli::DebugLine::<Endian>::new(&debug_line);
        let debug_info = gimli::DebugInfo::<Endian>::new(&debug_info);

        for unit in debug_info.units() {
            let unit = unit.expect("Should parse unit header OK");

            let abbrevs = unit.abbreviations(debug_abbrev)
                .expect("Error parsing abbreviations");

            let mut cursor = unit.entries(&abbrevs);
            cursor.next_dfs().expect("Should parse next dfs");

            let root = cursor.current().expect("Should have a root DIE");
            let value = root.attr_value(gimli::DW_AT_stmt_list);
            let offset = gimli::DebugLineOffset(match value {
                Some(gimli::AttributeValue::Data(data)) if data.len() == 4 => {
                    Endian::read_u32(data) as u64
                }
                Some(gimli::AttributeValue::Data(data)) if data.len() == 8 => {
                    Endian::read_u64(data)
                }
                Some(gimli::AttributeValue::SecOffset(offset)) => offset,
                _ => continue,
            });

            let header =
                gimli::LineNumberProgramHeader::new(debug_line, offset, unit.address_size());
            if let Ok(header) = header {
                println!("");
                println!("Offset:                             0x{:x}", offset.0);
                println!("Length:                             {}",
                         header.unit_length());
                println!("DWARF version:                      {}", header.version());
                println!("Prologue length:                    {}",
                         header.header_length());
                println!("Minimum instruction length:         {}",
                         header.minimum_instruction_length());
                println!("Maximum operations per instruction: {}",
                         header.maximum_operations_per_instruction());
                println!("Default is_stmt:                    {}",
                         header.default_is_stmt());
                println!("Line base:                          {}", header.line_base());
                println!("Line range:                         {}",
                         header.line_range());
                println!("Opcode base:                        {}",
                         header.opcode_base());

                println!("");
                println!("Opcodes:");
                for (i, length) in header.standard_opcode_lengths().iter().enumerate() {
                    println!("  Opcode {} as {} args", i + 1, length);
                }

                println!("");
                println!("The Directory Table:");
                for (i, dir) in header.include_directories().iter().enumerate() {
                    println!("  {} {}", i + 1, dir.to_string_lossy());
                }

                println!("");
                println!("The File Name Table");
                println!("  Entry\tDir\tTime\tSize\tName");
                for (i, file) in header.file_names().iter().enumerate() {
                    println!("  {}\t{}\t{}\t{}\t{}",
                             i + 1,
                             file.directory_index(),
                             file.last_modification(),
                             file.length(),
                             file.path_name().to_string_lossy());
                }

                println!("");
                println!("Line Number Statements:");
                let mut opcodes = header.opcodes();
                while let Some(opcode) = opcodes.next_opcode().expect("Should parse opcode OK") {
                    println!("  {}", opcode);
                }
            }
        }
    }
}

// All cross platform / object file format compatibility stuff should
// be contained in the `obj` module. Each supported platform / object
// file format should implement the `obj` module with an identical
// interface, but with the `pub type File` changing as needed. Hooray
// duck typing!

#[cfg(target_os="linux")]
mod obj {
    extern crate elf;
    use std::path::Path;

    /// The parsed object file type.
    pub type File = elf::File;

    /// Open and parse the object file at the given path.
    pub fn open<P>(path: P) -> File
        where P: AsRef<Path>
    {
        let path = path.as_ref();
        elf::File::open_path(path).expect("Could not open file")
    }

    /// Get the contents of the section named `section_name`, if such
    /// a section exists.
    pub fn get_section<'a>(file: &'a File, section_name: &str) -> Option<&'a [u8]> {
        file.sections
            .iter()
            .find(|s| s.shdr.name == section_name)
            .map(|s| &s.data[..])
    }

    /// Return true if the file is little endian, false if it is big endian.
    pub fn is_little_endian(file: &File) -> bool {
        match file.ehdr.data {
            elf::types::ELFDATA2LSB => true,
            elf::types::ELFDATA2MSB => false,
            otherwise => panic!("Unknown endianity: {}", otherwise),
        }
    }
}

#[cfg(target_os="macos")]
mod obj {
    extern crate mach_o;

    use std::ffi::CString;
    use std::fs;
    use std::io::Read;
    use std::mem;
    use std::path::Path;

    pub type File = Vec<u8>;

    pub fn open<P>(path: P) -> File
        where P: AsRef<Path>
    {
        let mut file = fs::File::open(path).expect("Could not open file");
        let mut buf = Vec::new();
        file.read_to_end(&mut buf).expect("Could not read file");
        buf
    }

    // Translate the "." prefix to the "__" prefix used by OSX/Mach-O, eg
    // ".debug_info" to "__debug_info".
    fn translate_section_name(section_name: &str) -> CString {
        let mut name = Vec::with_capacity(section_name.len() + 1);
        name.push(b'_');
        name.push(b'_');
        for ch in &section_name.as_bytes()[1..] {
            name.push(*ch);
        }
        unsafe { CString::from_vec_unchecked(name) }
    }

    pub fn get_section<'a>(file: &'a File, section_name: &str) -> Option<&'a [u8]> {
        let parsed = mach_o::Header::new(&file[..]).expect("Could not parse macho-o file");

        let segment_name = CString::new("__DWARF").unwrap();
        let section_name = translate_section_name(section_name);
        parsed.get_section(&segment_name, &section_name).map(|s| s.data())
    }

    pub fn is_little_endian(file: &File) -> bool {
        let parsed = mach_o::Header::new(&file[..]).expect("Could not parse macho-o file");

        let bytes = [1, 0, 0, 0u8];
        let int: u32 = unsafe { mem::transmute(bytes) };
        let native_byteorder_is_little = int == 1;

        match (native_byteorder_is_little, parsed.is_native_byteorder()) {
            (true, b) => b,
            (false, b) => !b,
        }
    }
}
<|endoftext|>


<commit_before>//! Routine to compute the strongly connected components (SCCs) of a
//! graph, as well as the resulting DAG if each SCC is replaced with a
//! node in the graph. This uses Tarjan's algorithm that completes in
//! O(n) time.

use crate::fx::FxHashSet;
use crate::graph::vec_graph::VecGraph;
use crate::graph::{DirectedGraph, GraphSuccessors, WithNumEdges, WithNumNodes, WithSuccessors};
use rustc_index::vec::{Idx, IndexVec};
use std::ops::Range;

#[cfg(test)]
mod tests;

/// Strongly connected components (SCC) of a graph. The type `N` is
/// the index type for the graph nodes and `S` is the index type for
/// the SCCs. We can map from each node to the SCC that it
/// participates in, and we also have the successors of each SCC.
pub struct Sccs<N: Idx, S: Idx> {
    /// For each node, what is the SCC index of the SCC to which it
    /// belongs.
    scc_indices: IndexVec<N, S>,

    /// Data about each SCC.
    scc_data: SccData<S>,
}

struct SccData<S: Idx> {
    /// For each SCC, the range of `all_successors` where its
    /// successors can be found.
    ranges: IndexVec<S, Range<usize>>,

    /// Contains the successors for all the Sccs, concatenated. The
    /// range of indices corresponding to a given SCC is found in its
    /// SccData.
    all_successors: Vec<S>,
}

impl<N: Idx, S: Idx> Sccs<N, S> {
    pub fn new(graph: &(impl DirectedGraph<Node = N> + WithNumNodes + WithSuccessors)) -> Self {
        SccsConstruction::construct(graph)
    }

    /// Returns the number of SCCs in the graph.
    pub fn num_sccs(&self) -> usize {
        self.scc_data.len()
    }

    /// Returns an iterator over the SCCs in the graph.
    ///
    /// The SCCs will be iterated in **dependency order** (or **post order**),
    /// meaning that if `S1 -> S2`, we will visit `S2` first and `S1` after.
    /// This is convenient when the edges represent dependencies: when you visit
    /// `S1`, the value for `S2` will already have been computed.
    pub fn all_sccs(&self) -> impl Iterator<Item = S> {
        (0..self.scc_data.len()).map(S::new)
    }

    /// Returns the SCC to which a node `r` belongs.
    pub fn scc(&self, r: N) -> S {
        self.scc_indices[r]
    }

    /// Returns the successors of the given SCC.
    pub fn successors(&self, scc: S) -> &[S] {
        self.scc_data.successors(scc)
    }

    /// Construct the reverse graph of the SCC graph.
    pub fn reverse(&self) -> VecGraph<S> {
        VecGraph::new(
            self.num_sccs(),
            self.all_sccs()
                .flat_map(|source| {
                    self.successors(source).iter().map(move |&target| (target, source))
                })
                .collect(),
        )
    }
}

impl<N: Idx, S: Idx> DirectedGraph for Sccs<N, S> {
    type Node = S;
}

impl<N: Idx, S: Idx> WithNumNodes for Sccs<N, S> {
    fn num_nodes(&self) -> usize {
        self.num_sccs()
    }
}

impl<N: Idx, S: Idx> WithNumEdges for Sccs<N, S> {
    fn num_edges(&self) -> usize {
        self.scc_data.all_successors.len()
    }
}

impl<N: Idx, S: Idx> GraphSuccessors<'graph> for Sccs<N, S> {
    type Item = S;

    type Iter = std::iter::Cloned<std::slice::Iter<'graph, S>>;
}

impl<N: Idx, S: Idx> WithSuccessors for Sccs<N, S> {
    fn successors(&self, node: S) -> <Self as GraphSuccessors<'_>>::Iter {
        self.successors(node).iter().cloned()
    }
}

impl<S: Idx> SccData<S> {
    /// Number of SCCs,
    fn len(&self) -> usize {
        self.ranges.len()
    }

    /// Returns the successors of the given SCC.
    fn successors(&self, scc: S) -> &[S] {
        // Annoyingly, `range` does not implement `Copy`, so we have
        // to do `range.start..range.end`:
        let range = &self.ranges[scc];
        &self.all_successors[range.start..range.end]
    }

    /// Creates a new SCC with `successors` as its successors and
    /// returns the resulting index.
    fn create_scc(&mut self, successors: impl IntoIterator<Item = S>) -> S {
        // Store the successors on `scc_successors_vec`, remembering
        // the range of indices.
        let all_successors_start = self.all_successors.len();
        self.all_successors.extend(successors);
        let all_successors_end = self.all_successors.len();

        debug!(
            "create_scc({:?}) successors={:?}",
            self.ranges.len(),
            &self.all_successors[all_successors_start..all_successors_end],
        );

        self.ranges.push(all_successors_start..all_successors_end)
    }
}

struct SccsConstruction<'c, G: DirectedGraph + WithNumNodes + WithSuccessors, S: Idx> {
    graph: &'c G,

    /// The state of each node; used during walk to record the stack
    /// and after walk to record what cycle each node ended up being
    /// in.
    node_states: IndexVec<G::Node, NodeState<G::Node, S>>,

    /// The stack of nodes that we are visiting as part of the DFS.
    node_stack: Vec<G::Node>,

    /// The stack of successors: as we visit a node, we mark our
    /// position in this stack, and when we encounter a successor SCC,
    /// we push it on the stack. When we complete an SCC, we can pop
    /// everything off the stack that was found along the way.
    successors_stack: Vec<S>,

    /// A set used to strip duplicates. As we accumulate successors
    /// into the successors_stack, we sometimes get duplicate entries.
    /// We use this set to remove those -- we also keep its storage
    /// around between successors to amortize memory allocation costs.
    duplicate_set: FxHashSet<S>,

    scc_data: SccData<S>,
}

#[derive(Copy, Clone, Debug)]
enum NodeState<N, S> {
    /// This node has not yet been visited as part of the DFS.
    ///
    /// After SCC construction is complete, this state ought to be
    /// impossible.
    NotVisited,

    /// This node is currently being walk as part of our DFS. It is on
    /// the stack at the depth `depth`.
    ///
    /// After SCC construction is complete, this state ought to be
    /// impossible.
    BeingVisited { depth: usize },

    /// Indicates that this node is a member of the given cycle.
    InCycle { scc_index: S },

    /// Indicates that this node is a member of whatever cycle
    /// `parent` is a member of. This state is transient: whenever we
    /// see it, we try to overwrite it with the current state of
    /// `parent` (this is the "path compression" step of a union-find
    /// algorithm).
    InCycleWith { parent: N },
}

#[derive(Copy, Clone, Debug)]
enum WalkReturn<S> {
    Cycle { min_depth: usize },
    Complete { scc_index: S },
}

impl<'c, G, S> SccsConstruction<'c, G, S>
where
    G: DirectedGraph + WithNumNodes + WithSuccessors,
    S: Idx,
{
    /// Identifies SCCs in the graph `G` and computes the resulting
    /// DAG. This uses a variant of [Tarjan's
    /// algorithm][wikipedia]. The high-level summary of the algorithm
    /// is that we do a depth-first search. Along the way, we keep a
    /// stack of each node whose successors are being visited. We
    /// track the depth of each node on this stack (there is no depth
    /// if the node is not on the stack). When we find that some node
    /// N with depth D can reach some other node N' with lower depth
    /// D' (i.e., D' < D), we know that N, N', and all nodes in
    /// between them on the stack are part of an SCC.
    ///
    /// [wikipedia]: https://bit.ly/2EZIx84
    fn construct(graph: &'c G) -> Sccs<G::Node, S> {
        let num_nodes = graph.num_nodes();

        let mut this = Self {
            graph,
            node_states: IndexVec::from_elem_n(NodeState::NotVisited, num_nodes),
            node_stack: Vec::with_capacity(num_nodes),
            successors_stack: Vec::new(),
            scc_data: SccData { ranges: IndexVec::new(), all_successors: Vec::new() },
            duplicate_set: FxHashSet::default(),
        };

        let scc_indices = (0..num_nodes)
            .map(G::Node::new)
            .map(|node| match this.walk_node(0, node) {
                WalkReturn::Complete { scc_index } => scc_index,
                WalkReturn::Cycle { min_depth } => {
                    panic!("`walk_node(0, {:?})` returned cycle with depth {:?}", node, min_depth)
                }
            })
            .collect();

        Sccs { scc_indices, scc_data: this.scc_data }
    }

    /// Visits a node during the DFS. We first examine its current
    /// state -- if it is not yet visited (`NotVisited`), we can push
    /// it onto the stack and start walking its successors.
    ///
    /// If it is already on the DFS stack it will be in the state
    /// `BeingVisited`. In that case, we have found a cycle and we
    /// return the depth from the stack.
    ///
    /// Otherwise, we are looking at a node that has already been
    /// completely visited. We therefore return `WalkReturn::Complete`
    /// with its associated SCC index.
    fn walk_node(&mut self, depth: usize, node: G::Node) -> WalkReturn<S> {
        debug!("walk_node(depth = {:?}, node = {:?})", depth, node);
        match self.find_state(node) {
            NodeState::InCycle { scc_index } => WalkReturn::Complete { scc_index },

            NodeState::BeingVisited { depth: min_depth } => WalkReturn::Cycle { min_depth },

            NodeState::NotVisited => self.walk_unvisited_node(depth, node),

            NodeState::InCycleWith { parent } => panic!(
                "`find_state` returned `InCycleWith({:?})`, which ought to be impossible",
                parent
            ),
        }
    }

    /// Fetches the state of the node `r`. If `r` is recorded as being
    /// in a cycle with some other node `r2`, then fetches the state
    /// of `r2` (and updates `r` to reflect current result). This is
    /// basically the "find" part of a standard union-find algorithm
    /// (with path compression).
    fn find_state(&mut self, r: G::Node) -> NodeState<G::Node, S> {
        debug!("find_state(r = {:?} in state {:?})", r, self.node_states[r]);
        match self.node_states[r] {
            NodeState::InCycle { scc_index } => NodeState::InCycle { scc_index },
            NodeState::BeingVisited { depth } => NodeState::BeingVisited { depth },
            NodeState::NotVisited => NodeState::NotVisited,
            NodeState::InCycleWith { parent } => {
                let parent_state = self.find_state(parent);
                debug!("find_state: parent_state = {:?}", parent_state);
                match parent_state {
                    NodeState::InCycle { .. } => {
                        self.node_states[r] = parent_state;
                        parent_state
                    }

                    NodeState::BeingVisited { depth } => {
                        self.node_states[r] =
                            NodeState::InCycleWith { parent: self.node_stack[depth] };
                        parent_state
                    }

                    NodeState::NotVisited | NodeState::InCycleWith { .. } => {
                        panic!("invalid parent state: {:?}", parent_state)
                    }
                }
            }
        }
    }

    /// Walks a node that has never been visited before.
    fn walk_unvisited_node(&mut self, depth: usize, node: G::Node) -> WalkReturn<S> {
        debug!("walk_unvisited_node(depth = {:?}, node = {:?})", depth, node);

        debug_assert!(match self.node_states[node] {
            NodeState::NotVisited => true,
            _ => false,
        });

        // Push `node` onto the stack.
        self.node_states[node] = NodeState::BeingVisited { depth };
        self.node_stack.push(node);

        // Walk each successor of the node, looking to see if any of
        // them can reach a node that is presently on the stack. If
        // so, that means they can also reach us.
        let mut min_depth = depth;
        let mut min_cycle_root = node;
        let successors_len = self.successors_stack.len();
        for successor_node in self.graph.successors(node) {
            debug!("walk_unvisited_node: node = {:?} successor_ode = {:?}", node, successor_node);
            match self.walk_node(depth + 1, successor_node) {
                WalkReturn::Cycle { min_depth: successor_min_depth } => {
                    // Track the minimum depth we can reach.
                    assert!(successor_min_depth <= depth);
                    if successor_min_depth < min_depth {
                        debug!(
                            "walk_unvisited_node: node = {:?} successor_min_depth = {:?}",
                            node, successor_min_depth
                        );
                        min_depth = successor_min_depth;
                        min_cycle_root = successor_node;
                    }
                }

                WalkReturn::Complete { scc_index: successor_scc_index } => {
                    // Push the completed SCC indices onto
                    // the `successors_stack` for later.
                    debug!(
                        "walk_unvisited_node: node = {:?} successor_scc_index = {:?}",
                        node, successor_scc_index
                    );
                    self.successors_stack.push(successor_scc_index);
                }
            }
        }

        // Completed walk, remove `node` from the stack.
        let r = self.node_stack.pop();
        debug_assert_eq!(r, Some(node));

        // If `min_depth == depth`, then we are the root of the
        // cycle: we can't reach anyone further down the stack.
        if min_depth == depth {
            // Note that successor stack may have duplicates, so we
            // want to remove those:
            let deduplicated_successors = {
                let duplicate_set = &mut self.duplicate_set;
                duplicate_set.clear();
                self.successors_stack
                    .drain(successors_len..)
                    .filter(move |&i| duplicate_set.insert(i))
            };
            let scc_index = self.scc_data.create_scc(deduplicated_successors);
            self.node_states[node] = NodeState::InCycle { scc_index };
            WalkReturn::Complete { scc_index }
        } else {
            // We are not the head of the cycle. Return back to our
            // caller. They will take ownership of the
            // `self.successors` data that we pushed.
            self.node_states[node] = NodeState::InCycleWith { parent: min_cycle_root };
            WalkReturn::Cycle { min_depth }
        }
    }
}
<commit_msg>Rollup merge of #73984 - pierwill:pierwill-tarjan, r=jonas-schievink<commit_after>//! Routine to compute the strongly connected components (SCCs) of a graph.
//!
//! Also computes as the resulting DAG if each SCC is replaced with a
//! node in the graph. This uses [Tarjan's algorithm](
//! https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm)
//! that completes in *O(n)* time.

use crate::fx::FxHashSet;
use crate::graph::vec_graph::VecGraph;
use crate::graph::{DirectedGraph, GraphSuccessors, WithNumEdges, WithNumNodes, WithSuccessors};
use rustc_index::vec::{Idx, IndexVec};
use std::ops::Range;

#[cfg(test)]
mod tests;

/// Strongly connected components (SCC) of a graph. The type `N` is
/// the index type for the graph nodes and `S` is the index type for
/// the SCCs. We can map from each node to the SCC that it
/// participates in, and we also have the successors of each SCC.
pub struct Sccs<N: Idx, S: Idx> {
    /// For each node, what is the SCC index of the SCC to which it
    /// belongs.
    scc_indices: IndexVec<N, S>,

    /// Data about each SCC.
    scc_data: SccData<S>,
}

struct SccData<S: Idx> {
    /// For each SCC, the range of `all_successors` where its
    /// successors can be found.
    ranges: IndexVec<S, Range<usize>>,

    /// Contains the successors for all the Sccs, concatenated. The
    /// range of indices corresponding to a given SCC is found in its
    /// SccData.
    all_successors: Vec<S>,
}

impl<N: Idx, S: Idx> Sccs<N, S> {
    pub fn new(graph: &(impl DirectedGraph<Node = N> + WithNumNodes + WithSuccessors)) -> Self {
        SccsConstruction::construct(graph)
    }

    /// Returns the number of SCCs in the graph.
    pub fn num_sccs(&self) -> usize {
        self.scc_data.len()
    }

    /// Returns an iterator over the SCCs in the graph.
    ///
    /// The SCCs will be iterated in **dependency order** (or **post order**),
    /// meaning that if `S1 -> S2`, we will visit `S2` first and `S1` after.
    /// This is convenient when the edges represent dependencies: when you visit
    /// `S1`, the value for `S2` will already have been computed.
    pub fn all_sccs(&self) -> impl Iterator<Item = S> {
        (0..self.scc_data.len()).map(S::new)
    }

    /// Returns the SCC to which a node `r` belongs.
    pub fn scc(&self, r: N) -> S {
        self.scc_indices[r]
    }

    /// Returns the successors of the given SCC.
    pub fn successors(&self, scc: S) -> &[S] {
        self.scc_data.successors(scc)
    }

    /// Construct the reverse graph of the SCC graph.
    pub fn reverse(&self) -> VecGraph<S> {
        VecGraph::new(
            self.num_sccs(),
            self.all_sccs()
                .flat_map(|source| {
                    self.successors(source).iter().map(move |&target| (target, source))
                })
                .collect(),
        )
    }
}

impl<N: Idx, S: Idx> DirectedGraph for Sccs<N, S> {
    type Node = S;
}

impl<N: Idx, S: Idx> WithNumNodes for Sccs<N, S> {
    fn num_nodes(&self) -> usize {
        self.num_sccs()
    }
}

impl<N: Idx, S: Idx> WithNumEdges for Sccs<N, S> {
    fn num_edges(&self) -> usize {
        self.scc_data.all_successors.len()
    }
}

impl<N: Idx, S: Idx> GraphSuccessors<'graph> for Sccs<N, S> {
    type Item = S;

    type Iter = std::iter::Cloned<std::slice::Iter<'graph, S>>;
}

impl<N: Idx, S: Idx> WithSuccessors for Sccs<N, S> {
    fn successors(&self, node: S) -> <Self as GraphSuccessors<'_>>::Iter {
        self.successors(node).iter().cloned()
    }
}

impl<S: Idx> SccData<S> {
    /// Number of SCCs,
    fn len(&self) -> usize {
        self.ranges.len()
    }

    /// Returns the successors of the given SCC.
    fn successors(&self, scc: S) -> &[S] {
        // Annoyingly, `range` does not implement `Copy`, so we have
        // to do `range.start..range.end`:
        let range = &self.ranges[scc];
        &self.all_successors[range.start..range.end]
    }

    /// Creates a new SCC with `successors` as its successors and
    /// returns the resulting index.
    fn create_scc(&mut self, successors: impl IntoIterator<Item = S>) -> S {
        // Store the successors on `scc_successors_vec`, remembering
        // the range of indices.
        let all_successors_start = self.all_successors.len();
        self.all_successors.extend(successors);
        let all_successors_end = self.all_successors.len();

        debug!(
            "create_scc({:?}) successors={:?}",
            self.ranges.len(),
            &self.all_successors[all_successors_start..all_successors_end],
        );

        self.ranges.push(all_successors_start..all_successors_end)
    }
}

struct SccsConstruction<'c, G: DirectedGraph + WithNumNodes + WithSuccessors, S: Idx> {
    graph: &'c G,

    /// The state of each node; used during walk to record the stack
    /// and after walk to record what cycle each node ended up being
    /// in.
    node_states: IndexVec<G::Node, NodeState<G::Node, S>>,

    /// The stack of nodes that we are visiting as part of the DFS.
    node_stack: Vec<G::Node>,

    /// The stack of successors: as we visit a node, we mark our
    /// position in this stack, and when we encounter a successor SCC,
    /// we push it on the stack. When we complete an SCC, we can pop
    /// everything off the stack that was found along the way.
    successors_stack: Vec<S>,

    /// A set used to strip duplicates. As we accumulate successors
    /// into the successors_stack, we sometimes get duplicate entries.
    /// We use this set to remove those -- we also keep its storage
    /// around between successors to amortize memory allocation costs.
    duplicate_set: FxHashSet<S>,

    scc_data: SccData<S>,
}

#[derive(Copy, Clone, Debug)]
enum NodeState<N, S> {
    /// This node has not yet been visited as part of the DFS.
    ///
    /// After SCC construction is complete, this state ought to be
    /// impossible.
    NotVisited,

    /// This node is currently being walk as part of our DFS. It is on
    /// the stack at the depth `depth`.
    ///
    /// After SCC construction is complete, this state ought to be
    /// impossible.
    BeingVisited { depth: usize },

    /// Indicates that this node is a member of the given cycle.
    InCycle { scc_index: S },

    /// Indicates that this node is a member of whatever cycle
    /// `parent` is a member of. This state is transient: whenever we
    /// see it, we try to overwrite it with the current state of
    /// `parent` (this is the "path compression" step of a union-find
    /// algorithm).
    InCycleWith { parent: N },
}

#[derive(Copy, Clone, Debug)]
enum WalkReturn<S> {
    Cycle { min_depth: usize },
    Complete { scc_index: S },
}

impl<'c, G, S> SccsConstruction<'c, G, S>
where
    G: DirectedGraph + WithNumNodes + WithSuccessors,
    S: Idx,
{
    /// Identifies SCCs in the graph `G` and computes the resulting
    /// DAG. This uses a variant of [Tarjan's
    /// algorithm][wikipedia]. The high-level summary of the algorithm
    /// is that we do a depth-first search. Along the way, we keep a
    /// stack of each node whose successors are being visited. We
    /// track the depth of each node on this stack (there is no depth
    /// if the node is not on the stack). When we find that some node
    /// N with depth D can reach some other node N' with lower depth
    /// D' (i.e., D' < D), we know that N, N', and all nodes in
    /// between them on the stack are part of an SCC.
    ///
    /// [wikipedia]: https://bit.ly/2EZIx84
    fn construct(graph: &'c G) -> Sccs<G::Node, S> {
        let num_nodes = graph.num_nodes();

        let mut this = Self {
            graph,
            node_states: IndexVec::from_elem_n(NodeState::NotVisited, num_nodes),
            node_stack: Vec::with_capacity(num_nodes),
            successors_stack: Vec::new(),
            scc_data: SccData { ranges: IndexVec::new(), all_successors: Vec::new() },
            duplicate_set: FxHashSet::default(),
        };

        let scc_indices = (0..num_nodes)
            .map(G::Node::new)
            .map(|node| match this.walk_node(0, node) {
                WalkReturn::Complete { scc_index } => scc_index,
                WalkReturn::Cycle { min_depth } => {
                    panic!("`walk_node(0, {:?})` returned cycle with depth {:?}", node, min_depth)
                }
            })
            .collect();

        Sccs { scc_indices, scc_data: this.scc_data }
    }

    /// Visits a node during the DFS. We first examine its current
    /// state -- if it is not yet visited (`NotVisited`), we can push
    /// it onto the stack and start walking its successors.
    ///
    /// If it is already on the DFS stack it will be in the state
    /// `BeingVisited`. In that case, we have found a cycle and we
    /// return the depth from the stack.
    ///
    /// Otherwise, we are looking at a node that has already been
    /// completely visited. We therefore return `WalkReturn::Complete`
    /// with its associated SCC index.
    fn walk_node(&mut self, depth: usize, node: G::Node) -> WalkReturn<S> {
        debug!("walk_node(depth = {:?}, node = {:?})", depth, node);
        match self.find_state(node) {
            NodeState::InCycle { scc_index } => WalkReturn::Complete { scc_index },

            NodeState::BeingVisited { depth: min_depth } => WalkReturn::Cycle { min_depth },

            NodeState::NotVisited => self.walk_unvisited_node(depth, node),

            NodeState::InCycleWith { parent } => panic!(
                "`find_state` returned `InCycleWith({:?})`, which ought to be impossible",
                parent
            ),
        }
    }

    /// Fetches the state of the node `r`. If `r` is recorded as being
    /// in a cycle with some other node `r2`, then fetches the state
    /// of `r2` (and updates `r` to reflect current result). This is
    /// basically the "find" part of a standard union-find algorithm
    /// (with path compression).
    fn find_state(&mut self, r: G::Node) -> NodeState<G::Node, S> {
        debug!("find_state(r = {:?} in state {:?})", r, self.node_states[r]);
        match self.node_states[r] {
            NodeState::InCycle { scc_index } => NodeState::InCycle { scc_index },
            NodeState::BeingVisited { depth } => NodeState::BeingVisited { depth },
            NodeState::NotVisited => NodeState::NotVisited,
            NodeState::InCycleWith { parent } => {
                let parent_state = self.find_state(parent);
                debug!("find_state: parent_state = {:?}", parent_state);
                match parent_state {
                    NodeState::InCycle { .. } => {
                        self.node_states[r] = parent_state;
                        parent_state
                    }

                    NodeState::BeingVisited { depth } => {
                        self.node_states[r] =
                            NodeState::InCycleWith { parent: self.node_stack[depth] };
                        parent_state
                    }

                    NodeState::NotVisited | NodeState::InCycleWith { .. } => {
                        panic!("invalid parent state: {:?}", parent_state)
                    }
                }
            }
        }
    }

    /// Walks a node that has never been visited before.
    fn walk_unvisited_node(&mut self, depth: usize, node: G::Node) -> WalkReturn<S> {
        debug!("walk_unvisited_node(depth = {:?}, node = {:?})", depth, node);

        debug_assert!(match self.node_states[node] {
            NodeState::NotVisited => true,
            _ => false,
        });

        // Push `node` onto the stack.
        self.node_states[node] = NodeState::BeingVisited { depth };
        self.node_stack.push(node);

        // Walk each successor of the node, looking to see if any of
        // them can reach a node that is presently on the stack. If
        // so, that means they can also reach us.
        let mut min_depth = depth;
        let mut min_cycle_root = node;
        let successors_len = self.successors_stack.len();
        for successor_node in self.graph.successors(node) {
            debug!("walk_unvisited_node: node = {:?} successor_ode = {:?}", node, successor_node);
            match self.walk_node(depth + 1, successor_node) {
                WalkReturn::Cycle { min_depth: successor_min_depth } => {
                    // Track the minimum depth we can reach.
                    assert!(successor_min_depth <= depth);
                    if successor_min_depth < min_depth {
                        debug!(
                            "walk_unvisited_node: node = {:?} successor_min_depth = {:?}",
                            node, successor_min_depth
                        );
                        min_depth = successor_min_depth;
                        min_cycle_root = successor_node;
                    }
                }

                WalkReturn::Complete { scc_index: successor_scc_index } => {
                    // Push the completed SCC indices onto
                    // the `successors_stack` for later.
                    debug!(
                        "walk_unvisited_node: node = {:?} successor_scc_index = {:?}",
                        node, successor_scc_index
                    );
                    self.successors_stack.push(successor_scc_index);
                }
            }
        }

        // Completed walk, remove `node` from the stack.
        let r = self.node_stack.pop();
        debug_assert_eq!(r, Some(node));

        // If `min_depth == depth`, then we are the root of the
        // cycle: we can't reach anyone further down the stack.
        if min_depth == depth {
            // Note that successor stack may have duplicates, so we
            // want to remove those:
            let deduplicated_successors = {
                let duplicate_set = &mut self.duplicate_set;
                duplicate_set.clear();
                self.successors_stack
                    .drain(successors_len..)
                    .filter(move |&i| duplicate_set.insert(i))
            };
            let scc_index = self.scc_data.create_scc(deduplicated_successors);
            self.node_states[node] = NodeState::InCycle { scc_index };
            WalkReturn::Complete { scc_index }
        } else {
            // We are not the head of the cycle. Return back to our
            // caller. They will take ownership of the
            // `self.successors` data that we pushed.
            self.node_states[node] = NodeState::InCycleWith { parent: min_cycle_root };
            WalkReturn::Cycle { min_depth }
        }
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017-2018 Ben Ashford
 *
 * 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.
 */

extern crate futures;
#[macro_use]
extern crate redis_async;

use std::sync::Arc;
use std::env;

use futures::{future, Future};

use redis_async::client;

// An artificial "realistic" non-trivial example to demonstrate usage
fn main() {
    // Create some completely arbitrary "test data"
    let test_data_size = 10;
    let test_data: Vec<_> = (0..test_data_size).map(|x| (x, x.to_string())).collect();

    // TODO - uncomment
    // let addr = env::args()
    //     .nth(1)
    //     .unwrap_or("127.0.0.1:6379".to_string())
    //     .parse()
    //     .unwrap();

    // let test_f = client::paired_connect(&addr);

    // let send_data = test_f.and_then(|connection| {
    //     let connection = Arc::new(connection);
    //     let futures = test_data.into_iter().map(move |data| {
    //         let connection_inner = connection.clone();
    //         connection
    //             .send(resp_array!["INCR", "realistic_test_ctr"])
    //             .and_then(move |ctr: String| {
    //                 let key = format!("rt_{}", ctr);
    //                 let d_val = data.0.to_string();
    //                 faf!(connection_inner.send(resp_array!["SET", &key, d_val]));
    //                 connection_inner.send(resp_array!["SET", data.1, key])
    //             })
    //     });
    //     future::join_all(futures)
    // });

    // let result: Vec<String> = core.run(send_data).unwrap();
    // assert_eq!(result.len(), test_data_size);
}
<commit_msg>Re-enable Realistic example<commit_after>/*
 * Copyright 2017-2018 Ben Ashford
 *
 * 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.
 */

extern crate futures;
#[macro_use]
extern crate redis_async;
extern crate tokio;

use std::sync::Arc;
use std::env;

use futures::{future, Future};
use futures::sync::oneshot;

use tokio::executor::current_thread;

use redis_async::client;

// An artificial "realistic" non-trivial example to demonstrate usage
fn main() {
    // Create some completely arbitrary "test data"
    let test_data_size = 10;
    let test_data: Vec<_> = (0..test_data_size).map(|x| (x, x.to_string())).collect();

    let addr = env::args()
        .nth(1)
        .unwrap_or("127.0.0.1:6379".to_string())
        .parse()
        .unwrap();

    let test_f = client::paired_connect(&addr, current_thread::task_executor());
    let (tx, rx) = oneshot::channel();

    let send_data = test_f.and_then(|connection| {
        let connection = Arc::new(connection);
        let futures = test_data.into_iter().map(move |data| {
            let connection_inner = connection.clone();
            connection
                .send(resp_array!["INCR", "realistic_test_ctr"])
                .and_then(move |ctr: String| {
                    let key = format!("rt_{}", ctr);
                    let d_val = data.0.to_string();
                    faf!(connection_inner.send(resp_array!["SET", &key, d_val]));
                    connection_inner.send(resp_array!["SET", data.1, key])
                })
        });
        future::join_all(futures)
    });
    let deliver = send_data.then(|result| match result {
        Ok(result) => match tx.send(result) {
            Ok(_) => future::ok(()),
            Err(e) => {
                println!("Unexpected error: {:?}", e);
                future::err(())
            }
        },
        Err(e) => {
            println!("Unexpected error: {:?}", e);
            future::err(())
        }
    });

    current_thread::run(|_| current_thread::spawn(deliver));

    let result: Vec<String> = rx.wait().expect("Waiting for delivery");
    assert_eq!(result.len(), test_data_size);
}
<|endoftext|>


<commit_before>// Simple API example, ported from http://lua-users.org/wiki/SimpleLuaApiExample

// This is a simple introductory example of how to interface to Lua from Rust.
// The Rust program loads a Lua script file, sets some Lua variables, runs the
// Lua script, and reads back the return value.

#![allow(non_snake_case)]
#![feature(path,io,os,core)]

extern crate lua;

use std::old_io as io;
use std::os;
use std::iter::range_inclusive;

fn main() {
    let mut L = lua::State::new();
    L.openlibs(); // Load Lua libraries

    // Load the file containing the script we are going to run
    let path = Path::new("simpleapi.lua");
    match L.loadfile(Some(&path)) {
        Ok(_) => (),
        Err(_) => {
            // If something went wrong, error message is at the top of the stack
            let _ = writeln!(&mut io::stderr(),
                             "Couldn't load file: {}", L.describe(-1));
            os::set_exit_status(1);
            return;
        }
    }

    /*
     * Ok, now here we go: We pass data to the lua script on the stack.
     * That is, we first have to prepare Lua's virtual stack the way we
     * want the script to receive it, then ask Lua to run it.
     */
    L.newtable(); // We will pass a table

    /*
     * To put values into the table, we first push the index, then the
     * value, and then call rawset() with the index of the table in the
     * stack. Let's see why it's -3: In Lua, the value -1 always refers to
     * the top of the stack. When you create the table with newtable(),
     * the table gets pushed into the top of the stack. When you push the
     * index and then the cell value, the stack looks like:
     *
     * - [stack bottom] -- table, index, value [top]
     *
     * So the -1 will refer to the cell value, thus -3 is used to refer to
     * the table itself. Note that rawset() pops the last two elements
     * of the stack, so that after it has been called, the table is at the
     * top of the stack.
     */
    for i in range_inclusive(1, 5) {
        L.pushinteger(i);   // Push the table index
        L.pushinteger(i*2); // Push the cell value
        L.rawset(-3);       // Stores the pair in the table
    }

    // By what name is the script going to reference our table?
    L.setglobal("foo");

    // Ask Lua to run our little script
    match L.pcall(0, lua::MULTRET, 0) {
        Ok(()) => (),
        Err(_) => {
            let _ = writeln!(&mut io::stderr(),
                             "Failed to run script: {}", L.describe(-1));
            os::set_exit_status(1);
            return;
        }
    }

    // Get the returned value at the to of the stack (index -1)
    let sum = L.tonumber(-1);

    println!("Script returned: {}", sum);

    L.pop(1); // Take the returned value out of the stack

    // L's destructor will close the state for us
}
<commit_msg>s/os/env/<commit_after>// Simple API example, ported from http://lua-users.org/wiki/SimpleLuaApiExample

// This is a simple introductory example of how to interface to Lua from Rust.
// The Rust program loads a Lua script file, sets some Lua variables, runs the
// Lua script, and reads back the return value.

#![allow(non_snake_case)]
#![feature(path,io,env,core)]

extern crate lua;

use std::old_io as io;
use std::env;
use std::iter::range_inclusive;

fn main() {
    let mut L = lua::State::new();
    L.openlibs(); // Load Lua libraries

    // Load the file containing the script we are going to run
    let path = Path::new("simpleapi.lua");
    match L.loadfile(Some(&path)) {
        Ok(_) => (),
        Err(_) => {
            // If something went wrong, error message is at the top of the stack
            let _ = writeln!(&mut io::stderr(),
                             "Couldn't load file: {}", L.describe(-1));
            env::set_exit_status(1);
            return;
        }
    }

    /*
     * Ok, now here we go: We pass data to the lua script on the stack.
     * That is, we first have to prepare Lua's virtual stack the way we
     * want the script to receive it, then ask Lua to run it.
     */
    L.newtable(); // We will pass a table

    /*
     * To put values into the table, we first push the index, then the
     * value, and then call rawset() with the index of the table in the
     * stack. Let's see why it's -3: In Lua, the value -1 always refers to
     * the top of the stack. When you create the table with newtable(),
     * the table gets pushed into the top of the stack. When you push the
     * index and then the cell value, the stack looks like:
     *
     * - [stack bottom] -- table, index, value [top]
     *
     * So the -1 will refer to the cell value, thus -3 is used to refer to
     * the table itself. Note that rawset() pops the last two elements
     * of the stack, so that after it has been called, the table is at the
     * top of the stack.
     */
    for i in range_inclusive(1, 5) {
        L.pushinteger(i);   // Push the table index
        L.pushinteger(i*2); // Push the cell value
        L.rawset(-3);       // Stores the pair in the table
    }

    // By what name is the script going to reference our table?
    L.setglobal("foo");

    // Ask Lua to run our little script
    match L.pcall(0, lua::MULTRET, 0) {
        Ok(()) => (),
        Err(_) => {
            let _ = writeln!(&mut io::stderr(),
                             "Failed to run script: {}", L.describe(-1));
            env::set_exit_status(1);
            return;
        }
    }

    // Get the returned value at the to of the stack (index -1)
    let sum = L.tonumber(-1);

    println!("Script returned: {}", sum);

    L.pop(1); // Take the returned value out of the stack

    // L's destructor will close the state for us
}
<|endoftext|>


<commit_before>use hir::{HasSource, InFile};
use ide_db::assists::{AssistId, AssistKind};
use syntax::{
    ast::{self, edit::IndentLevel},
    AstNode, TextSize,
};

use crate::assist_context::{AssistContext, Assists};

// Assist: generate_enum_variant
//
// Adds a variant to an enum.
//
// ```
// enum Countries {
//     Ghana,
// }
//
// fn main() {
//     let country = Countries::Lesotho$0;
// }
// ```
// ->
// ```
// enum Countries {
//     Ghana,
//     Lesotho,
// }
//
// fn main() {
//     let country = Countries::Lesotho;
// }
// ```
pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
    let path = path_expr.path()?;

    if ctx.sema.resolve_path(&path).is_some() {
        // No need to generate anything if the path resolves
        return None;
    }

    let name_ref = path.segment()?.name_ref()?;
    if name_ref.text().starts_with(char::is_lowercase) {
        // Don't suggest generating variant if the name starts with a lowercase letter
        return None;
    }

    if let Some(hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e)))) =
        ctx.sema.resolve_path(&path.qualifier()?)
    {
        let target = path.syntax().text_range();
        return add_variant_to_accumulator(acc, ctx, target, e, &name_ref);
    }

    None
}

fn add_variant_to_accumulator(
    acc: &mut Assists,
    ctx: &AssistContext,
    target: syntax::TextRange,
    adt: hir::Enum,
    name_ref: &ast::NameRef,
) -> Option<()> {
    let db = ctx.db();
    let InFile { file_id, value: enum_node } = adt.source(db)?.original_ast_node(db)?;
    let enum_indent = IndentLevel::from_node(&enum_node.syntax());

    let variant_list = enum_node.variant_list()?;
    let offset = variant_list.syntax().text_range().end() - TextSize::of('}');
    let empty_enum = variant_list.variants().next().is_none();

    acc.add(
        AssistId("generate_enum_variant", AssistKind::Generate),
        "Generate variant",
        target,
        |builder| {
            builder.edit_file(file_id.original_file(db));
            let text = format!(
                "{maybe_newline}{indent_1}{name},\n{enum_indent}",
                maybe_newline = if empty_enum { "\n" } else { "" },
                indent_1 = IndentLevel(1),
                name = name_ref,
                enum_indent = enum_indent
            );
            builder.insert(offset, text)
        },
    )
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn generate_basic_enum_variant_in_empty_enum() {
        check_assist(
            generate_enum_variant,
            r"
enum Foo {}
fn main() {
    Foo::Bar$0
}
",
            r"
enum Foo {
    Bar,
}
fn main() {
    Foo::Bar
}
",
        )
    }

    #[test]
    fn generate_basic_enum_variant_in_non_empty_enum() {
        check_assist(
            generate_enum_variant,
            r"
enum Foo {
    Bar,
}
fn main() {
    Foo::Baz$0
}
",
            r"
enum Foo {
    Bar,
    Baz,
}
fn main() {
    Foo::Baz
}
",
        )
    }

    #[test]
    fn generate_basic_enum_variant_in_different_file() {
        check_assist(
            generate_enum_variant,
            r"
//- /main.rs
mod foo;
use foo::Foo;

fn main() {
    Foo::Baz$0
}

//- /foo.rs
enum Foo {
    Bar,
}
",
            r"
//- /main.rs
mod foo;
use foo::Foo;

fn main() {
    Foo::Baz
}

//- /foo.rs
enum Foo {
    Bar,
    Baz,
}

",
        )
    }

    #[test]
    fn not_applicable_for_existing_variant() {
        check_assist_not_applicable(
            generate_enum_variant,
            r"
enum Foo {
    Bar,
}
fn main() {
    Foo::Bar$0
}
",
        )
    }

    #[test]
    fn not_applicable_for_lowercase() {
        check_assist_not_applicable(
            generate_enum_variant,
            r"
enum Foo {
    Bar,
}
fn main() {
    Foo::new$0
}
",
        )
    }

    #[test]
    fn indentation_level_is_correct() {
        check_assist(
            generate_enum_variant,
            r"
mod m {
    enum Foo {
        Bar,
    }
}
fn main() {
    m::Foo::Baz$0
}
",
            r"
mod m {
    enum Foo {
        Bar,
        Baz,
    }
}
fn main() {
    m::Foo::Baz
}
",
        )
    }
}
<commit_msg>Make test pass<commit_after>use hir::{HasSource, InFile};
use ide_db::assists::{AssistId, AssistKind};
use syntax::{
    ast::{self, edit::IndentLevel},
    AstNode, TextSize,
};

use crate::assist_context::{AssistContext, Assists};

// Assist: generate_enum_variant
//
// Adds a variant to an enum.
//
// ```
// enum Countries {
//     Ghana,
// }
//
// fn main() {
//     let country = Countries::Lesotho$0;
// }
// ```
// ->
// ```
// enum Countries {
//     Ghana,
//     Lesotho,
// }
//
// fn main() {
//     let country = Countries::Lesotho;
// }
// ```
pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
    let path = path_expr.path()?;

    if ctx.sema.resolve_path(&path).is_some() {
        // No need to generate anything if the path resolves
        return None;
    }

    let name_ref = path.segment()?.name_ref()?;
    if name_ref.text().starts_with(char::is_lowercase) {
        // Don't suggest generating variant if the name starts with a lowercase letter
        return None;
    }

    if let Some(hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e)))) =
        ctx.sema.resolve_path(&path.qualifier()?)
    {
        let target = path.syntax().text_range();
        return add_variant_to_accumulator(acc, ctx, target, e, &name_ref);
    }

    None
}

fn add_variant_to_accumulator(
    acc: &mut Assists,
    ctx: &AssistContext,
    target: syntax::TextRange,
    adt: hir::Enum,
    name_ref: &ast::NameRef,
) -> Option<()> {
    let db = ctx.db();
    let InFile { file_id, value: enum_node } = adt.source(db)?.original_ast_node(db)?;
    let enum_indent = IndentLevel::from_node(&enum_node.syntax());

    let variant_list = enum_node.variant_list()?;
    let offset = variant_list.syntax().text_range().end() - TextSize::of('}');
    let empty_enum = variant_list.variants().next().is_none();

    acc.add(
        AssistId("generate_enum_variant", AssistKind::Generate),
        "Generate variant",
        target,
        |builder| {
            builder.edit_file(file_id.original_file(db));
            let text = format!(
                "{maybe_newline}{indent_1}{name},\n{enum_indent}",
                maybe_newline = if empty_enum { "\n" } else { "" },
                indent_1 = IndentLevel(1),
                name = name_ref,
                enum_indent = enum_indent
            );
            builder.insert(offset, text)
        },
    )
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn generate_basic_enum_variant_in_empty_enum() {
        check_assist(
            generate_enum_variant,
            r"
enum Foo {}
fn main() {
    Foo::Bar$0
}
",
            r"
enum Foo {
    Bar,
}
fn main() {
    Foo::Bar
}
",
        )
    }

    #[test]
    fn generate_basic_enum_variant_in_non_empty_enum() {
        check_assist(
            generate_enum_variant,
            r"
enum Foo {
    Bar,
}
fn main() {
    Foo::Baz$0
}
",
            r"
enum Foo {
    Bar,
    Baz,
}
fn main() {
    Foo::Baz
}
",
        )
    }

    #[test]
    fn generate_basic_enum_variant_in_different_file() {
        check_assist(
            generate_enum_variant,
            r"
//- /main.rs
mod foo;
use foo::Foo;

fn main() {
    Foo::Baz$0
}

//- /foo.rs
enum Foo {
    Bar,
}
",
            r"
enum Foo {
    Bar,
    Baz,
}
",
        )
    }

    #[test]
    fn not_applicable_for_existing_variant() {
        check_assist_not_applicable(
            generate_enum_variant,
            r"
enum Foo {
    Bar,
}
fn main() {
    Foo::Bar$0
}
",
        )
    }

    #[test]
    fn not_applicable_for_lowercase() {
        check_assist_not_applicable(
            generate_enum_variant,
            r"
enum Foo {
    Bar,
}
fn main() {
    Foo::new$0
}
",
        )
    }

    #[test]
    fn indentation_level_is_correct() {
        check_assist(
            generate_enum_variant,
            r"
mod m {
    enum Foo {
        Bar,
    }
}
fn main() {
    m::Foo::Baz$0
}
",
            r"
mod m {
    enum Foo {
        Bar,
        Baz,
    }
}
fn main() {
    m::Foo::Baz
}
",
        )
    }
}
<|endoftext|>


<commit_before>//! The module for the automatic map generation

use noise::{Brownian2, Seed, open_simplex2};

// TODO pick:
// use core::*;
// use core::{Map, Tile, TileMap};
use core::{Block, Tile, TileMap};

// Probably really buggy code...

/// A map
///
/// Note: It's chunked into big chunks and small chunks.
///       Small chunks keeps the noise data. Big chunks
///       determines the overlay layer.
pub struct MapGenerator<'a> {
  seed: &'a Seed,
}

/// Types of big chunks
enum BChunkType {
  /// Reserved for history, empty per default, but gets opened as the game is played. Manually designed.
  History,
  /// Automatic map generated chunk
  Auto,
  /// Manually random generated chunk
  Manually,
  /// Empty chunck
  Empty,
}

// TODO: Find out how to prevent double islands (manually generated islands)

impl<'a> MapGenerator<'a> {
  /// Creates a new map
  pub fn new(seed: &'a Seed) -> MapGenerator<'a> {
    MapGenerator {
      seed: seed,
    }
  }

  // TODO: Add some sort of cache

  /// Get the noise value at a given point
  fn get_noise_value(&self, x: i64, y: i64) -> f64 {
    let noise = Brownian2::new(open_simplex2, 4).wavelength(32.0);
    noise.apply(&self.seed, &[x as f64, y as f64])
  }

  /// Get big chunk coordinates
  fn get_bchunk(x: i64, y: i64) -> (i64, i64) {
    // TODO: Chunk size 64?
    (((x as f64) / 64.0).floor() as i64, ((y as f64) / 64.0).floor() as i64)
  }

  /// Get the chunk type of the big chunk at (x, y)
  fn get_bchunk_type(&self, x: i64, y: i64) -> BChunkType {
    let noise = Brownian2::new(open_simplex2, 4).wavelength(32.0);
    let noise_val = noise.apply(&self.seed, &[x as f64, y as f64]);
    if noise_val > 0.3 {
      BChunkType::Empty // Too many too few?
    } else if noise_val > 0.1 {
      BChunkType::Auto
    } else if noise_val > 0.05 {
      BChunkType::History
    } else {
      BChunkType::Manually
    }
  }

  /// Get overlay value (used for customizing the noise)
  fn get_overlay_value(&self, x: i64, y: i64) -> f64 {
    // TODO
    // Stuff here
    1.0
  }
}

impl<'a> TileMap for MapGenerator<'a> {

  // Add foreground/background
  /// Get the tile at a given point
  fn get_tile(&self, x: i64, y: i64) -> Block {
    let val = (self.get_noise_value(x, y) + self.get_overlay_value(x, y)) / 2.0;

    // Temporary map gen. Proof of concept.
    if val > 0.9 {
      Block::new(Tile::Water, Tile::Grass, Tile::Tree)
    } else {
      Block::new(Tile::Water, Tile::None, Tile::None)
    }
  }
}
<commit_msg>Update mapgen<commit_after>//! The module for the automatic map generation

use noise::{Brownian2, Seed, open_simplex2};

// TODO pick:
// use core::*;
// use core::{Map, Tile, TileMap};
use core::{Block, Tile, TileMap};

// Probably really buggy code...

/// A map
///
/// Note: It's chunked into big chunks and small chunks.
///       Small chunks keeps the noise data. Big chunks
///       determines the overlay layer.
pub struct MapGenerator<'a> {
  seed: &'a Seed,
}

/// Types of big chunks
pub enum BChunkType {
  /// Reserved for history, empty per default, but gets opened as the game is played. Manually designed.
  History,
  /// Automatic map generated chunk
  Auto,
  /// Manually random generated chunk
  Manually,
  /// Empty chunk
  Empty,
}

// TODO: Find out how to prevent double islands (manually generated islands)

impl<'a> MapGenerator<'a> {
  /// Creates a new map
  pub fn new(seed: &'a Seed) -> MapGenerator<'a> {
    MapGenerator {
      seed: seed,
    }
  }

  // TODO: Add some sort of cache

  /// Get the noise value at a given point
  pub fn get_noise_value(&self, x: i64, y: i64) -> f64 {
    let noise = Brownian2::new(open_simplex2, 4).wavelength(32.0);
    noise.apply(&self.seed, &[x as f64, y as f64])
  }

  /// Get big chunk coordinates
  pub fn get_bchunk(x: i64, y: i64) -> (i64, i64) {
    // TODO: Chunk size 64?
    (((x as f64) / 64.0).floor() as i64, ((y as f64) / 64.0).floor() as i64)
  }

  /// Get the chunk type of the big chunk at (x, y)
  pub fn get_bchunk_type(&self, x: i64, y: i64) -> BChunkType {
    let noise = Brownian2::new(open_simplex2, 4).wavelength(32.0);
    let noise_val = noise.apply(&self.seed, &[x as f64, y as f64]);
    if noise_val > 0.3 {
      BChunkType::Empty // Too many too few?
    } else if noise_val > 0.1 {
      BChunkType::Auto
    } else if noise_val > 0.05 {
      BChunkType::History
    } else {
      BChunkType::Manually
    }
  }

  /// Get overlay value (used for customizing the noise)
  pub fn get_overlay_value(&self, x: i64, y: i64) -> f64 {
    let (cx, cy) = MapGenerator::get_bchunk(x, y);
    let chunk_type = self.get_bchunk_type(cx, cy);
    match chunk_type {
      BChunkType::Empty => 0.0,
      BChunkType::Auto => {
        // This is why we need vector math, kids.

        // TODO: Make more shapes.
        let elip_dist = (((cx - x) * (cx - x) + (cy - y) * (cy - y)) as f64).sqrt();

        let res = 25.0 - elip_dist;

        if res > 0.0 {
          res
        } else {
          0.0
        }
      },
      _ => 0.0,
    }
  }
}

impl<'a> TileMap for MapGenerator<'a> {

  // Add foreground/background
  /// Get the tile at a given point
  fn get_tile(&self, x: i64, y: i64) -> Block {
    let val = (self.get_noise_value(x, y) + self.get_overlay_value(x, y)) / 2.0;

    // Temporary map gen. Proof of concept.
    if val > 0.9 {
      Block::new(Tile::Water, Tile::Grass, Tile::Tree)
    } else {
      Block::new(Tile::Water, Tile::None, Tile::None)
    }
  }
}
<|endoftext|>