<commit_before>//! Interpreter for compiled code.

use cell_gc::GcHeapSession;
use cell_gc::collections::VecRef;
use compile::{op, CodeRef};
use env::{Environment, EnvironmentRef, StaticEnvironmentRef};
use errors::Result;
use value::{Lambda, Pair, Value};

/// A potentially partially evaluated value.
///
/// When the interpreter calls a builtin, usually the builtin runs, computes a
/// Value, and returns it. But a small number of builtins (`apply`, `eval`) can
/// tail-call Scheme code. This must be done without recursively invoking the
/// interpreter. That would consume some system stack space; it wouldn't be a
/// true tail call. So instead, the builtin returns a special Trampoline to the
/// interpreter that means "I did the part of the computation that's written in
/// Rust; the rest is Scheme code and here's how to run it". The word "returns"
/// is the key: the stack space that was expended to call the builtin is
/// reclaimed when it returns.
pub enum Trampoline<'h> {
    /// A completely evaluated value.
    Value(Value<'h>),

    /// A value that can be computed by calling a particular procedure.
    TailCall {
        func: Value<'h>,
        args: Vec<Value<'h>>,
    },

    /// A value that can be computed by running the given code.
    TailEval {
        code: CodeRef<'h>,
        env: EnvironmentRef<'h>
    }
}

fn new_call_env<'h>(
    hs: &mut GcHeapSession<'h>,
    senv: StaticEnvironmentRef<'h>,
    has_rest: bool,
    parent: EnvironmentRef<'h>,
    mut args: Vec<Value<'h>>,
) -> Result<EnvironmentRef<'h>> {
    let names = senv.names();
    let n_names = names.len();
    let n_required_params = n_names - has_rest as usize;

    let n_actual = args.len();
    if n_actual < n_required_params {
        return Err("not enough arguments".into());
    }
    if has_rest {
        let mut rest_list = Value::Nil;
        for arg in args.drain(n_required_params..).rev() {
            rest_list = Value::Cons(hs.alloc(Pair {
                car: arg,
                cdr: rest_list,
            }));
        }
        args.push(rest_list);
    } else if n_actual > n_required_params {
        return Err("too many arguments".into());
    }

    let args = hs.alloc(args);  // move values into heap
    Ok(Environment::new(hs, Some(parent), senv, args))
}

pub fn apply<'h>(
    hs: &mut GcHeapSession<'h>,
    mut fval: Value<'h>,
    mut args: Vec<Value<'h>>,
) -> Result<Value<'h>> {
    loop {
        match fval {
            Value::Builtin(f) => {
                match (f.0)(hs, args)? {
                    Trampoline::Value(v) => return Ok(v),
                    Trampoline::TailCall { func: new_fval, args: new_args } => {
                        fval = new_fval;
                        args = new_args;
                    }
                    Trampoline::TailEval { code, env } => return eval_compiled(hs, &env, code),
                }
            }

            Value::Lambda(lambda) => {
                let code = lambda.code();
                let parent = lambda.env();
                let senv = code.environments().get(0);
                let names = senv.names();
                let n_names = names.len();
                let has_rest = code.rest();

                let n_required_params = n_names - has_rest as usize;
                if args.len() < n_required_params {
                    return Err("not enough arguments".into());
                }
                if has_rest {
                    let mut rest_list = Value::Nil;
                    for v in args.drain(n_required_params..).rev() {
                        rest_list = Value::Cons(hs.alloc(Pair {
                            car: v,
                            cdr: rest_list,
                        }));
                    }
                    args.push(rest_list);
                } else if args.len() > n_required_params {
                    return Err("too many arguments".into());
                }

                let values = hs.alloc(args);
                let env = Environment::new(hs, Some(parent), senv, values);
                return eval_compiled(hs, &env, code);
            }

            _ => {
                return Err("not a procedure".into());
            }
        }
    }
}

// Fully evaluate the given code in the given environment.
pub fn eval_compiled<'h>(
    hs: &mut GcHeapSession<'h>,
    env: &EnvironmentRef<'h>,
    mut code: CodeRef<'h>,
) -> Result<Value<'h>> {
    assert_eq!(code.environments().get(0), env.senv(),
               "code can only run in the environment for which it was compiled");

    // The stack is a sequence of concatenated stack frames. Each frame is laid out like this:
    //   #(code pc env operand0 ...)

    let mut stack = Vec::<Value<'h>>::with_capacity(1024);
    let mut constants = code.constants();
    let mut environments = code.environments();
    let mut insns = code.insns();
    let mut env = env.clone();
    let mut pc = 0;

    macro_rules! return_value {
        ($value:ident) => {
            if stack.is_empty() {
                return Ok($value);
            }

            // Read the caller's stack frame back into local variables.
            env = stack.pop().unwrap().as_environment("internal error").unwrap();
            pc = stack.pop().unwrap().as_int("internal error").unwrap() as usize;
            code = stack.pop().unwrap().as_code("internal error").unwrap();
            constants = code.constants();
            environments = code.environments();
            insns = code.insns();

            //println!("resuming at pc={}", pc);
            //code.dump();

            // Push the return value to its operand stack.
            stack.push($value);
        }
    }

    loop {
        let op_code = insns.get(pc);
        pc += 1;
        //println!("{} op {}", pc - 1, op_code);
        match op_code {
            op::RETURN => {
                // The return value is the last thing left on the current
                // frame's operand stack.
                let value = stack.pop().unwrap();
                return_value!(value);
            }

            op::POP => {
                stack.pop().unwrap();
            }

            op::CONSTANT => {
                let i = insns.get(pc) as usize;
                pc += 1;
                stack.push(constants.get(i));
            }

            op::GET_DYNAMIC => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let symbol = match constants.get(i) {
                    Value::Symbol(id) => id,
                    _ => panic!("internal error: bad GetDynamic insn"),
                };
                stack.push(env.dynamic_get(&symbol)?);
            }

            op::GET_STATIC => {
                let up_count = insns.get(pc) as usize;
                pc += 1;
                let i = insns.get(pc) as usize;
                pc += 1;
                stack.push(env.get(up_count, i));
            }

            op::LAMBDA => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let fn_code = match constants.get(i) {
                    Value::Code(code) => code,
                    _ => panic!("internal error: bad Lambda insn"),
                };
                let fn_value = Value::Lambda(hs.alloc(Lambda {
                    code: fn_code,
                    env: env.clone(),
                }));
                stack.push(fn_value);
            }

            op::CALL | op::TAIL_CALL => {
                let argc = insns.get(pc) as usize;
                pc += 1;

                let top = stack.len();
                let mut args = stack.split_off(top - argc);
                let mut fval = stack.pop().unwrap();

                loop {
                    match fval {
                        Value::Lambda(lambda) => {
                            if op_code == op::CALL {
                                stack.push(Value::Code(code));
                                assert!(pc <= i32::max_value() as usize);
                                stack.push(Value::Int(pc as i32));
                                stack.push(Value::Environment(env));
                            } else {
                                // Tail call. Our caller's frame is already on top
                                // of stack. Assert that it's correctly laid out.
                                let top = stack.len();
                                if top != 0 {
                                    assert!(stack[top - 3].is_code());
                                    assert!(stack[top - 2].is_int());
                                    assert!(stack[top - 1].is_environment());
                                }
                            }

                            code = lambda.code();
                            constants = code.constants();
                            environments = code.environments();
                            insns = code.insns();
                            pc = 0;
                            env = new_call_env(hs, code.environments().get(0), code.rest(), lambda.env(), args)?;
                            break;
                        }
                        Value::Builtin(f) => {
                            match (f.0)(hs, args)? {
                                Trampoline::Value(v) => {
                                    if op_code == op::TAIL_CALL {
                                        return_value!(v);
                                    } else {
                                        stack.push(v);
                                    }
                                    break;
                                }
                                Trampoline::TailCall { func: new_fval, args: new_args } => {
                                    fval = new_fval;
                                    args = new_args;
                                }
                                Trampoline::TailEval { code: eval_code, env: eval_env } => {
                                    code = eval_code;
                                    constants = code.constants();
                                    environments = code.environments();
                                    insns = code.insns();
                                    pc = 0;
                                    env = eval_env;
                                    break;
                                }
                            }
                        }
                        _ => return Err("not a procedure".into())
                    }
                }
            }

            op::JUMP_IF_FALSE => {
                let offset = match stack.pop().unwrap() {
                    Value::Bool(false) => insns.get(pc) as usize,
                    _ => 1
                };
                pc += offset;
            }

            op::JUMP => {
                pc += insns.get(pc) as usize;
            }

            op::DEFINE => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let symbol = match constants.get(i) {
                    Value::Symbol(id) => id,
                    _ => panic!("internal error: bad Define insn"),
                };
                let value = stack.pop().unwrap();
                env.define(symbol.unwrap(), value);
            }

            op::SET_STATIC => {
                let up_count = insns.get(pc) as usize;
                pc += 1;
                let i = insns.get(pc) as usize;
                pc += 1;
                env.set(up_count, i, stack.pop().unwrap());
            }

            op::SET_DYNAMIC => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let symbol = match constants.get(i) {
                    Value::Symbol(id) => id.unwrap(),
                    _ => panic!("internal error: bad Set insn"),
                };
                let value = stack.pop().unwrap();
                env.dynamic_set(&symbol, value)?;
            }

            op::PUSH_ENV => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let senv = environments.get(i);
                let values: VecRef<'h, Value<'h>> = hs.alloc(
                    (0..senv.names().len())
                        .map(|_| Value::Unspecified)
                        .collect::<Vec<Value<'h>>>(),
                );
                env = Environment::new(hs, Some(env), senv, values);
            }

            op::POP_ENV => {
                env = env.parent().unwrap();
            }

            op::SAVE => {
                // Capture the current function activation's continuation as a
                // vector, a snapshot of the stack. This opcode is used only in
                // call/cc.
                //
                // Note that this does not capture the continuation of the
                // current *instruction*, which would have to include the
                // current values of code, pc, and env. What call/cc needs to
                // capture is the continuation of call/cc.
                let v = hs.alloc(stack.clone());
                stack.push(Value::Vector(v));
            }

            op::RESTORE => {
                // Resume a previously captured continuation. The continuation
                // and the value we will pass to it are taken from just-so
                // locations in the current environment. This opcode is used
                // exclusively in the code for continuation-lambdas produced by
                // call/cc.
                let arg = env.get(0, 0);
                let new_stack = env.get(1, 0)
                    .as_vector("internal error").unwrap();
                stack.clear();
                stack.extend(new_stack);
                return_value!(arg);
            }

            _ => panic!("internal error: invalid opcode {}", op_code),
        }
    }
}
<commit_msg>lisp: Delete some commented-out print statements.<commit_after>//! Interpreter for compiled code.

use cell_gc::GcHeapSession;
use cell_gc::collections::VecRef;
use compile::{op, CodeRef};
use env::{Environment, EnvironmentRef, StaticEnvironmentRef};
use errors::Result;
use value::{Lambda, Pair, Value};

/// A potentially partially evaluated value.
///
/// When the interpreter calls a builtin, usually the builtin runs, computes a
/// Value, and returns it. But a small number of builtins (`apply`, `eval`) can
/// tail-call Scheme code. This must be done without recursively invoking the
/// interpreter. That would consume some system stack space; it wouldn't be a
/// true tail call. So instead, the builtin returns a special Trampoline to the
/// interpreter that means "I did the part of the computation that's written in
/// Rust; the rest is Scheme code and here's how to run it". The word "returns"
/// is the key: the stack space that was expended to call the builtin is
/// reclaimed when it returns.
pub enum Trampoline<'h> {
    /// A completely evaluated value.
    Value(Value<'h>),

    /// A value that can be computed by calling a particular procedure.
    TailCall {
        func: Value<'h>,
        args: Vec<Value<'h>>,
    },

    /// A value that can be computed by running the given code.
    TailEval {
        code: CodeRef<'h>,
        env: EnvironmentRef<'h>
    }
}

fn new_call_env<'h>(
    hs: &mut GcHeapSession<'h>,
    senv: StaticEnvironmentRef<'h>,
    has_rest: bool,
    parent: EnvironmentRef<'h>,
    mut args: Vec<Value<'h>>,
) -> Result<EnvironmentRef<'h>> {
    let names = senv.names();
    let n_names = names.len();
    let n_required_params = n_names - has_rest as usize;

    let n_actual = args.len();
    if n_actual < n_required_params {
        return Err("not enough arguments".into());
    }
    if has_rest {
        let mut rest_list = Value::Nil;
        for arg in args.drain(n_required_params..).rev() {
            rest_list = Value::Cons(hs.alloc(Pair {
                car: arg,
                cdr: rest_list,
            }));
        }
        args.push(rest_list);
    } else if n_actual > n_required_params {
        return Err("too many arguments".into());
    }

    let args = hs.alloc(args);  // move values into heap
    Ok(Environment::new(hs, Some(parent), senv, args))
}

pub fn apply<'h>(
    hs: &mut GcHeapSession<'h>,
    mut fval: Value<'h>,
    mut args: Vec<Value<'h>>,
) -> Result<Value<'h>> {
    loop {
        match fval {
            Value::Builtin(f) => {
                match (f.0)(hs, args)? {
                    Trampoline::Value(v) => return Ok(v),
                    Trampoline::TailCall { func: new_fval, args: new_args } => {
                        fval = new_fval;
                        args = new_args;
                    }
                    Trampoline::TailEval { code, env } => return eval_compiled(hs, &env, code),
                }
            }

            Value::Lambda(lambda) => {
                let code = lambda.code();
                let parent = lambda.env();
                let senv = code.environments().get(0);
                let names = senv.names();
                let n_names = names.len();
                let has_rest = code.rest();

                let n_required_params = n_names - has_rest as usize;
                if args.len() < n_required_params {
                    return Err("not enough arguments".into());
                }
                if has_rest {
                    let mut rest_list = Value::Nil;
                    for v in args.drain(n_required_params..).rev() {
                        rest_list = Value::Cons(hs.alloc(Pair {
                            car: v,
                            cdr: rest_list,
                        }));
                    }
                    args.push(rest_list);
                } else if args.len() > n_required_params {
                    return Err("too many arguments".into());
                }

                let values = hs.alloc(args);
                let env = Environment::new(hs, Some(parent), senv, values);
                return eval_compiled(hs, &env, code);
            }

            _ => {
                return Err("not a procedure".into());
            }
        }
    }
}

// Fully evaluate the given code in the given environment.
pub fn eval_compiled<'h>(
    hs: &mut GcHeapSession<'h>,
    env: &EnvironmentRef<'h>,
    mut code: CodeRef<'h>,
) -> Result<Value<'h>> {
    assert_eq!(code.environments().get(0), env.senv(),
               "code can only run in the environment for which it was compiled");

    // The stack is a sequence of concatenated stack frames. Each frame is laid out like this:
    //   #(code pc env operand0 ...)

    let mut stack = Vec::<Value<'h>>::with_capacity(1024);
    let mut constants = code.constants();
    let mut environments = code.environments();
    let mut insns = code.insns();
    let mut env = env.clone();
    let mut pc = 0;

    macro_rules! return_value {
        ($value:ident) => {
            if stack.is_empty() {
                return Ok($value);
            }

            // Read the caller's stack frame back into local variables.
            env = stack.pop().unwrap().as_environment("internal error").unwrap();
            pc = stack.pop().unwrap().as_int("internal error").unwrap() as usize;
            code = stack.pop().unwrap().as_code("internal error").unwrap();
            constants = code.constants();
            environments = code.environments();
            insns = code.insns();

            // Push the return value to its operand stack.
            stack.push($value);
        }
    }

    loop {
        let op_code = insns.get(pc);
        pc += 1;
        match op_code {
            op::RETURN => {
                // The return value is the last thing left on the current
                // frame's operand stack.
                let value = stack.pop().unwrap();
                return_value!(value);
            }

            op::POP => {
                stack.pop().unwrap();
            }

            op::CONSTANT => {
                let i = insns.get(pc) as usize;
                pc += 1;
                stack.push(constants.get(i));
            }

            op::GET_DYNAMIC => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let symbol = match constants.get(i) {
                    Value::Symbol(id) => id,
                    _ => panic!("internal error: bad GetDynamic insn"),
                };
                stack.push(env.dynamic_get(&symbol)?);
            }

            op::GET_STATIC => {
                let up_count = insns.get(pc) as usize;
                pc += 1;
                let i = insns.get(pc) as usize;
                pc += 1;
                stack.push(env.get(up_count, i));
            }

            op::LAMBDA => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let fn_code = match constants.get(i) {
                    Value::Code(code) => code,
                    _ => panic!("internal error: bad Lambda insn"),
                };
                let fn_value = Value::Lambda(hs.alloc(Lambda {
                    code: fn_code,
                    env: env.clone(),
                }));
                stack.push(fn_value);
            }

            op::CALL | op::TAIL_CALL => {
                let argc = insns.get(pc) as usize;
                pc += 1;

                let top = stack.len();
                let mut args = stack.split_off(top - argc);
                let mut fval = stack.pop().unwrap();

                loop {
                    match fval {
                        Value::Lambda(lambda) => {
                            if op_code == op::CALL {
                                stack.push(Value::Code(code));
                                assert!(pc <= i32::max_value() as usize);
                                stack.push(Value::Int(pc as i32));
                                stack.push(Value::Environment(env));
                            } else {
                                // Tail call. Our caller's frame is already on top
                                // of stack. Assert that it's correctly laid out.
                                let top = stack.len();
                                if top != 0 {
                                    assert!(stack[top - 3].is_code());
                                    assert!(stack[top - 2].is_int());
                                    assert!(stack[top - 1].is_environment());
                                }
                            }

                            code = lambda.code();
                            constants = code.constants();
                            environments = code.environments();
                            insns = code.insns();
                            pc = 0;
                            env = new_call_env(hs, code.environments().get(0), code.rest(), lambda.env(), args)?;
                            break;
                        }
                        Value::Builtin(f) => {
                            match (f.0)(hs, args)? {
                                Trampoline::Value(v) => {
                                    if op_code == op::TAIL_CALL {
                                        return_value!(v);
                                    } else {
                                        stack.push(v);
                                    }
                                    break;
                                }
                                Trampoline::TailCall { func: new_fval, args: new_args } => {
                                    fval = new_fval;
                                    args = new_args;
                                }
                                Trampoline::TailEval { code: eval_code, env: eval_env } => {
                                    code = eval_code;
                                    constants = code.constants();
                                    environments = code.environments();
                                    insns = code.insns();
                                    pc = 0;
                                    env = eval_env;
                                    break;
                                }
                            }
                        }
                        _ => return Err("not a procedure".into())
                    }
                }
            }

            op::JUMP_IF_FALSE => {
                let offset = match stack.pop().unwrap() {
                    Value::Bool(false) => insns.get(pc) as usize,
                    _ => 1
                };
                pc += offset;
            }

            op::JUMP => {
                pc += insns.get(pc) as usize;
            }

            op::DEFINE => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let symbol = match constants.get(i) {
                    Value::Symbol(id) => id,
                    _ => panic!("internal error: bad Define insn"),
                };
                let value = stack.pop().unwrap();
                env.define(symbol.unwrap(), value);
            }

            op::SET_STATIC => {
                let up_count = insns.get(pc) as usize;
                pc += 1;
                let i = insns.get(pc) as usize;
                pc += 1;
                env.set(up_count, i, stack.pop().unwrap());
            }

            op::SET_DYNAMIC => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let symbol = match constants.get(i) {
                    Value::Symbol(id) => id.unwrap(),
                    _ => panic!("internal error: bad Set insn"),
                };
                let value = stack.pop().unwrap();
                env.dynamic_set(&symbol, value)?;
            }

            op::PUSH_ENV => {
                let i = insns.get(pc) as usize;
                pc += 1;
                let senv = environments.get(i);
                let values: VecRef<'h, Value<'h>> = hs.alloc(
                    (0..senv.names().len())
                        .map(|_| Value::Unspecified)
                        .collect::<Vec<Value<'h>>>(),
                );
                env = Environment::new(hs, Some(env), senv, values);
            }

            op::POP_ENV => {
                env = env.parent().unwrap();
            }

            op::SAVE => {
                // Capture the current function activation's continuation as a
                // vector, a snapshot of the stack. This opcode is used only in
                // call/cc.
                //
                // Note that this does not capture the continuation of the
                // current *instruction*, which would have to include the
                // current values of code, pc, and env. What call/cc needs to
                // capture is the continuation of call/cc.
                let v = hs.alloc(stack.clone());
                stack.push(Value::Vector(v));
            }

            op::RESTORE => {
                // Resume a previously captured continuation. The continuation
                // and the value we will pass to it are taken from just-so
                // locations in the current environment. This opcode is used
                // exclusively in the code for continuation-lambdas produced by
                // call/cc.
                let arg = env.get(0, 0);
                let new_stack = env.get(1, 0)
                    .as_vector("internal error").unwrap();
                stack.clear();
                stack.extend(new_stack);
                return_value!(arg);
            }

            _ => panic!("internal error: invalid opcode {}", op_code),
        }
    }
}
<|endoftext|>


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

//! A simple application that uses glutin to open a window for Servo to display in.

#![feature(int_uint)]
#![feature(box_syntax)]
#![feature(old_io)]
#![feature(std_misc)]

#[macro_use] extern crate bitflags;
#[cfg(target_os="macos")]
extern crate cgl;
extern crate compositing;
extern crate geom;
extern crate gleam;
extern crate glutin;
extern crate layers;
extern crate libc;
extern crate msg;
#[cfg(feature = "window")] extern crate script_traits;
extern crate time;
extern crate util;
extern crate egl;
extern crate url;

use compositing::windowing::WindowEvent;
use geom::scale_factor::ScaleFactor;
use std::rc::Rc;
use window::Window;
use util::opts;

pub mod window;

pub trait NestedEventLoopListener {
    fn handle_event_from_nested_event_loop(&mut self, event: WindowEvent) -> bool;
}

pub fn create_window() -> Rc<Window> {
    // Read command-line options.
    let opts = opts::get();
    let foreground = opts.output_file.is_none();
    let scale_factor = opts.device_pixels_per_px.unwrap_or(ScaleFactor::new(1.0));
    let size = opts.initial_window_size.as_f32() * scale_factor;

    // Open a window.
    Window::new(foreground, size.as_uint().cast().unwrap())
}
<commit_msg>Only define feature gates when they're used.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! A simple application that uses glutin to open a window for Servo to display in.

#![feature(int_uint)]
#![feature(box_syntax)]
#![cfg_attr(all(feature = "window", target_os = "linux"), feature(old_io, std_misc))]

#[macro_use] extern crate bitflags;
#[cfg(target_os="macos")]
extern crate cgl;
extern crate compositing;
extern crate geom;
extern crate gleam;
extern crate glutin;
extern crate layers;
extern crate libc;
extern crate msg;
#[cfg(feature = "window")] extern crate script_traits;
extern crate time;
extern crate util;
extern crate egl;
extern crate url;

use compositing::windowing::WindowEvent;
use geom::scale_factor::ScaleFactor;
use std::rc::Rc;
use window::Window;
use util::opts;

pub mod window;

pub trait NestedEventLoopListener {
    fn handle_event_from_nested_event_loop(&mut self, event: WindowEvent) -> bool;
}

pub fn create_window() -> Rc<Window> {
    // Read command-line options.
    let opts = opts::get();
    let foreground = opts.output_file.is_none();
    let scale_factor = opts.device_pixels_per_px.unwrap_or(ScaleFactor::new(1.0));
    let size = opts.initial_window_size.as_f32() * scale_factor;

    // Open a window.
    Window::new(foreground, size.as_uint().cast().unwrap())
}
<|endoftext|>


<commit_before>// The visitors in this module collect sizes and counts of the most important
// pieces of AST and HIR. The resulting numbers are good approximations but not
// completely accurate (some things might be counted twice, others missed).

use rustc_ast::visit as ast_visit;
use rustc_ast::{self as ast, AttrId, NodeId};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::intravisit as hir_visit;
use rustc_hir::HirId;
use rustc_middle::hir::map::Map;
use rustc_middle::util::common::to_readable_str;
use rustc_span::Span;

#[derive(Copy, Clone, PartialEq, Eq, Hash)]
enum Id {
    Node(HirId),
    Attr(AttrId),
    None,
}

struct NodeData {
    count: usize,
    size: usize,
}

struct StatCollector<'k> {
    krate: Option<&'k hir::Crate<'k>>,
    data: FxHashMap<&'static str, NodeData>,
    seen: FxHashSet<Id>,
}

pub fn print_hir_stats(krate: &hir::Crate<'_>) {
    let mut collector = StatCollector {
        krate: Some(krate),
        data: FxHashMap::default(),
        seen: FxHashSet::default(),
    };
    hir_visit::walk_crate(&mut collector, krate);
    collector.print("HIR STATS");
}

pub fn print_ast_stats(krate: &ast::Crate, title: &str) {
    let mut collector =
        StatCollector { krate: None, data: FxHashMap::default(), seen: FxHashSet::default() };
    ast_visit::walk_crate(&mut collector, krate);
    collector.print(title);
}

impl<'k> StatCollector<'k> {
    fn record<T>(&mut self, label: &'static str, id: Id, node: &T) {
        if id != Id::None && !self.seen.insert(id) {
            return;
        }

        let entry = self.data.entry(label).or_insert(NodeData { count: 0, size: 0 });

        entry.count += 1;
        entry.size = std::mem::size_of_val(node);
    }

    fn print(&self, title: &str) {
        let mut stats: Vec<_> = self.data.iter().collect();

        stats.sort_by_key(|&(_, ref d)| d.count * d.size);

        let mut total_size = 0;

        eprintln!("\n{}\n", title);

        eprintln!("{:<18}{:>18}{:>14}{:>14}", "Name", "Accumulated Size", "Count", "Item Size");
        eprintln!("----------------------------------------------------------------");

        for (label, data) in stats {
            eprintln!(
                "{:<18}{:>18}{:>14}{:>14}",
                label,
                to_readable_str(data.count * data.size),
                to_readable_str(data.count),
                to_readable_str(data.size)
            );

            total_size += data.count * data.size;
        }
        eprintln!("----------------------------------------------------------------");
        eprintln!("{:<18}{:>18}\n", "Total", to_readable_str(total_size));
    }
}

impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
    fn visit_param(&mut self, param: &'v hir::Param<'v>) {
        self.record("Param", Id::Node(param.hir_id), param);
        hir_visit::walk_param(self, param)
    }

    type Map = Map<'v>;

    fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap<Self::Map> {
        panic!("visit_nested_xxx must be manually implemented in this visitor")
    }

    fn visit_nested_item(&mut self, id: hir::ItemId) {
        let nested_item = self.krate.unwrap().item(id);
        self.visit_item(nested_item)
    }

    fn visit_nested_trait_item(&mut self, trait_item_id: hir::TraitItemId) {
        let nested_trait_item = self.krate.unwrap().trait_item(trait_item_id);
        self.visit_trait_item(nested_trait_item)
    }

    fn visit_nested_impl_item(&mut self, impl_item_id: hir::ImplItemId) {
        let nested_impl_item = self.krate.unwrap().impl_item(impl_item_id);
        self.visit_impl_item(nested_impl_item)
    }

    fn visit_nested_body(&mut self, body_id: hir::BodyId) {
        let nested_body = self.krate.unwrap().body(body_id);
        self.visit_body(nested_body)
    }

    fn visit_item(&mut self, i: &'v hir::Item<'v>) {
        self.record("Item", Id::Node(i.hir_id()), i);
        hir_visit::walk_item(self, i)
    }

    fn visit_foreign_item(&mut self, i: &'v hir::ForeignItem<'v>) {
        self.record("ForeignItem", Id::Node(i.hir_id()), i);
        hir_visit::walk_foreign_item(self, i)
    }

    fn visit_local(&mut self, l: &'v hir::Local<'v>) {
        self.record("Local", Id::Node(l.hir_id), l);
        hir_visit::walk_local(self, l)
    }

    fn visit_block(&mut self, b: &'v hir::Block<'v>) {
        self.record("Block", Id::Node(b.hir_id), b);
        hir_visit::walk_block(self, b)
    }

    fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
        self.record("Stmt", Id::Node(s.hir_id), s);
        hir_visit::walk_stmt(self, s)
    }

    fn visit_arm(&mut self, a: &'v hir::Arm<'v>) {
        self.record("Arm", Id::Node(a.hir_id), a);
        hir_visit::walk_arm(self, a)
    }

    fn visit_pat(&mut self, p: &'v hir::Pat<'v>) {
        self.record("Pat", Id::Node(p.hir_id), p);
        hir_visit::walk_pat(self, p)
    }

    fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
        self.record("Expr", Id::Node(ex.hir_id), ex);
        hir_visit::walk_expr(self, ex)
    }

    fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
        self.record("Ty", Id::Node(t.hir_id), t);
        hir_visit::walk_ty(self, t)
    }

    fn visit_fn(
        &mut self,
        fk: hir_visit::FnKind<'v>,
        fd: &'v hir::FnDecl<'v>,
        b: hir::BodyId,
        s: Span,
        id: hir::HirId,
    ) {
        self.record("FnDecl", Id::None, fd);
        hir_visit::walk_fn(self, fk, fd, b, s, id)
    }

    fn visit_where_predicate(&mut self, predicate: &'v hir::WherePredicate<'v>) {
        self.record("WherePredicate", Id::None, predicate);
        hir_visit::walk_where_predicate(self, predicate)
    }

    fn visit_trait_item(&mut self, ti: &'v hir::TraitItem<'v>) {
        self.record("TraitItem", Id::Node(ti.hir_id()), ti);
        hir_visit::walk_trait_item(self, ti)
    }

    fn visit_impl_item(&mut self, ii: &'v hir::ImplItem<'v>) {
        self.record("ImplItem", Id::Node(ii.hir_id()), ii);
        hir_visit::walk_impl_item(self, ii)
    }

    fn visit_param_bound(&mut self, bounds: &'v hir::GenericBound<'v>) {
        self.record("GenericBound", Id::None, bounds);
        hir_visit::walk_param_bound(self, bounds)
    }

    fn visit_struct_field(&mut self, s: &'v hir::StructField<'v>) {
        self.record("StructField", Id::Node(s.hir_id), s);
        hir_visit::walk_struct_field(self, s)
    }

    fn visit_variant(
        &mut self,
        v: &'v hir::Variant<'v>,
        g: &'v hir::Generics<'v>,
        item_id: hir::HirId,
    ) {
        self.record("Variant", Id::None, v);
        hir_visit::walk_variant(self, v, g, item_id)
    }

    fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
        self.record("Lifetime", Id::Node(lifetime.hir_id), lifetime);
        hir_visit::walk_lifetime(self, lifetime)
    }

    fn visit_qpath(&mut self, qpath: &'v hir::QPath<'v>, id: hir::HirId, span: Span) {
        self.record("QPath", Id::None, qpath);
        hir_visit::walk_qpath(self, qpath, id, span)
    }

    fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) {
        self.record("Path", Id::None, path);
        hir_visit::walk_path(self, path)
    }

    fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v hir::PathSegment<'v>) {
        self.record("PathSegment", Id::None, path_segment);
        hir_visit::walk_path_segment(self, path_span, path_segment)
    }

    fn visit_assoc_type_binding(&mut self, type_binding: &'v hir::TypeBinding<'v>) {
        self.record("TypeBinding", Id::Node(type_binding.hir_id), type_binding);
        hir_visit::walk_assoc_type_binding(self, type_binding)
    }

    fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
        self.record("Attribute", Id::Attr(attr.id), attr);
    }

    fn visit_macro_def(&mut self, macro_def: &'v hir::MacroDef<'v>) {
        self.record("MacroDef", Id::Node(macro_def.hir_id()), macro_def);
        hir_visit::walk_macro_def(self, macro_def)
    }
}

impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
    fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
        self.record("ForeignItem", Id::None, i);
        ast_visit::walk_foreign_item(self, i)
    }

    fn visit_item(&mut self, i: &'v ast::Item) {
        self.record("Item", Id::None, i);
        ast_visit::walk_item(self, i)
    }

    fn visit_local(&mut self, l: &'v ast::Local) {
        self.record("Local", Id::None, l);
        ast_visit::walk_local(self, l)
    }

    fn visit_block(&mut self, b: &'v ast::Block) {
        self.record("Block", Id::None, b);
        ast_visit::walk_block(self, b)
    }

    fn visit_stmt(&mut self, s: &'v ast::Stmt) {
        self.record("Stmt", Id::None, s);
        ast_visit::walk_stmt(self, s)
    }

    fn visit_arm(&mut self, a: &'v ast::Arm) {
        self.record("Arm", Id::None, a);
        ast_visit::walk_arm(self, a)
    }

    fn visit_pat(&mut self, p: &'v ast::Pat) {
        self.record("Pat", Id::None, p);
        ast_visit::walk_pat(self, p)
    }

    fn visit_expr(&mut self, ex: &'v ast::Expr) {
        self.record("Expr", Id::None, ex);
        ast_visit::walk_expr(self, ex)
    }

    fn visit_ty(&mut self, t: &'v ast::Ty) {
        self.record("Ty", Id::None, t);
        ast_visit::walk_ty(self, t)
    }

    fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, s: Span, _: NodeId) {
        self.record("FnDecl", Id::None, fk.decl());
        ast_visit::walk_fn(self, fk, s)
    }

    fn visit_assoc_item(&mut self, item: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
        let label = match ctxt {
            ast_visit::AssocCtxt::Trait => "TraitItem",
            ast_visit::AssocCtxt::Impl => "ImplItem",
        };
        self.record(label, Id::None, item);
        ast_visit::walk_assoc_item(self, item, ctxt);
    }

    fn visit_param_bound(&mut self, bounds: &'v ast::GenericBound) {
        self.record("GenericBound", Id::None, bounds);
        ast_visit::walk_param_bound(self, bounds)
    }

    fn visit_struct_field(&mut self, s: &'v ast::StructField) {
        self.record("StructField", Id::None, s);
        ast_visit::walk_struct_field(self, s)
    }

    fn visit_variant(&mut self, v: &'v ast::Variant) {
        self.record("Variant", Id::None, v);
        ast_visit::walk_variant(self, v)
    }

    fn visit_lifetime(&mut self, lifetime: &'v ast::Lifetime) {
        self.record("Lifetime", Id::None, lifetime);
        ast_visit::walk_lifetime(self, lifetime)
    }

    fn visit_mac_call(&mut self, mac: &'v ast::MacCall) {
        self.record("MacCall", Id::None, mac);
        ast_visit::walk_mac(self, mac)
    }

    fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v ast::PathSegment) {
        self.record("PathSegment", Id::None, path_segment);
        ast_visit::walk_path_segment(self, path_span, path_segment)
    }

    fn visit_assoc_ty_constraint(&mut self, constraint: &'v ast::AssocTyConstraint) {
        self.record("AssocTyConstraint", Id::None, constraint);
        ast_visit::walk_assoc_ty_constraint(self, constraint)
    }

    fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
        self.record("Attribute", Id::None, attr);
    }
}
<commit_msg>Rollup merge of #82258 - tmiasko:foreign-hir-stats, r=davidtwco<commit_after>// The visitors in this module collect sizes and counts of the most important
// pieces of AST and HIR. The resulting numbers are good approximations but not
// completely accurate (some things might be counted twice, others missed).

use rustc_ast::visit as ast_visit;
use rustc_ast::{self as ast, AttrId, NodeId};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::intravisit as hir_visit;
use rustc_hir::HirId;
use rustc_middle::hir::map::Map;
use rustc_middle::util::common::to_readable_str;
use rustc_span::Span;

#[derive(Copy, Clone, PartialEq, Eq, Hash)]
enum Id {
    Node(HirId),
    Attr(AttrId),
    None,
}

struct NodeData {
    count: usize,
    size: usize,
}

struct StatCollector<'k> {
    krate: Option<&'k hir::Crate<'k>>,
    data: FxHashMap<&'static str, NodeData>,
    seen: FxHashSet<Id>,
}

pub fn print_hir_stats(krate: &hir::Crate<'_>) {
    let mut collector = StatCollector {
        krate: Some(krate),
        data: FxHashMap::default(),
        seen: FxHashSet::default(),
    };
    hir_visit::walk_crate(&mut collector, krate);
    collector.print("HIR STATS");
}

pub fn print_ast_stats(krate: &ast::Crate, title: &str) {
    let mut collector =
        StatCollector { krate: None, data: FxHashMap::default(), seen: FxHashSet::default() };
    ast_visit::walk_crate(&mut collector, krate);
    collector.print(title);
}

impl<'k> StatCollector<'k> {
    fn record<T>(&mut self, label: &'static str, id: Id, node: &T) {
        if id != Id::None && !self.seen.insert(id) {
            return;
        }

        let entry = self.data.entry(label).or_insert(NodeData { count: 0, size: 0 });

        entry.count += 1;
        entry.size = std::mem::size_of_val(node);
    }

    fn print(&self, title: &str) {
        let mut stats: Vec<_> = self.data.iter().collect();

        stats.sort_by_key(|&(_, ref d)| d.count * d.size);

        let mut total_size = 0;

        eprintln!("\n{}\n", title);

        eprintln!("{:<18}{:>18}{:>14}{:>14}", "Name", "Accumulated Size", "Count", "Item Size");
        eprintln!("----------------------------------------------------------------");

        for (label, data) in stats {
            eprintln!(
                "{:<18}{:>18}{:>14}{:>14}",
                label,
                to_readable_str(data.count * data.size),
                to_readable_str(data.count),
                to_readable_str(data.size)
            );

            total_size += data.count * data.size;
        }
        eprintln!("----------------------------------------------------------------");
        eprintln!("{:<18}{:>18}\n", "Total", to_readable_str(total_size));
    }
}

impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
    fn visit_param(&mut self, param: &'v hir::Param<'v>) {
        self.record("Param", Id::Node(param.hir_id), param);
        hir_visit::walk_param(self, param)
    }

    type Map = Map<'v>;

    fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap<Self::Map> {
        panic!("visit_nested_xxx must be manually implemented in this visitor")
    }

    fn visit_nested_item(&mut self, id: hir::ItemId) {
        let nested_item = self.krate.unwrap().item(id);
        self.visit_item(nested_item)
    }

    fn visit_nested_trait_item(&mut self, trait_item_id: hir::TraitItemId) {
        let nested_trait_item = self.krate.unwrap().trait_item(trait_item_id);
        self.visit_trait_item(nested_trait_item)
    }

    fn visit_nested_impl_item(&mut self, impl_item_id: hir::ImplItemId) {
        let nested_impl_item = self.krate.unwrap().impl_item(impl_item_id);
        self.visit_impl_item(nested_impl_item)
    }

    fn visit_nested_foreign_item(&mut self, id: hir::ForeignItemId) {
        let nested_foreign_item = self.krate.unwrap().foreign_item(id);
        self.visit_foreign_item(nested_foreign_item);
    }

    fn visit_nested_body(&mut self, body_id: hir::BodyId) {
        let nested_body = self.krate.unwrap().body(body_id);
        self.visit_body(nested_body)
    }

    fn visit_item(&mut self, i: &'v hir::Item<'v>) {
        self.record("Item", Id::Node(i.hir_id()), i);
        hir_visit::walk_item(self, i)
    }

    fn visit_foreign_item(&mut self, i: &'v hir::ForeignItem<'v>) {
        self.record("ForeignItem", Id::Node(i.hir_id()), i);
        hir_visit::walk_foreign_item(self, i)
    }

    fn visit_local(&mut self, l: &'v hir::Local<'v>) {
        self.record("Local", Id::Node(l.hir_id), l);
        hir_visit::walk_local(self, l)
    }

    fn visit_block(&mut self, b: &'v hir::Block<'v>) {
        self.record("Block", Id::Node(b.hir_id), b);
        hir_visit::walk_block(self, b)
    }

    fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
        self.record("Stmt", Id::Node(s.hir_id), s);
        hir_visit::walk_stmt(self, s)
    }

    fn visit_arm(&mut self, a: &'v hir::Arm<'v>) {
        self.record("Arm", Id::Node(a.hir_id), a);
        hir_visit::walk_arm(self, a)
    }

    fn visit_pat(&mut self, p: &'v hir::Pat<'v>) {
        self.record("Pat", Id::Node(p.hir_id), p);
        hir_visit::walk_pat(self, p)
    }

    fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
        self.record("Expr", Id::Node(ex.hir_id), ex);
        hir_visit::walk_expr(self, ex)
    }

    fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
        self.record("Ty", Id::Node(t.hir_id), t);
        hir_visit::walk_ty(self, t)
    }

    fn visit_fn(
        &mut self,
        fk: hir_visit::FnKind<'v>,
        fd: &'v hir::FnDecl<'v>,
        b: hir::BodyId,
        s: Span,
        id: hir::HirId,
    ) {
        self.record("FnDecl", Id::None, fd);
        hir_visit::walk_fn(self, fk, fd, b, s, id)
    }

    fn visit_where_predicate(&mut self, predicate: &'v hir::WherePredicate<'v>) {
        self.record("WherePredicate", Id::None, predicate);
        hir_visit::walk_where_predicate(self, predicate)
    }

    fn visit_trait_item(&mut self, ti: &'v hir::TraitItem<'v>) {
        self.record("TraitItem", Id::Node(ti.hir_id()), ti);
        hir_visit::walk_trait_item(self, ti)
    }

    fn visit_impl_item(&mut self, ii: &'v hir::ImplItem<'v>) {
        self.record("ImplItem", Id::Node(ii.hir_id()), ii);
        hir_visit::walk_impl_item(self, ii)
    }

    fn visit_param_bound(&mut self, bounds: &'v hir::GenericBound<'v>) {
        self.record("GenericBound", Id::None, bounds);
        hir_visit::walk_param_bound(self, bounds)
    }

    fn visit_struct_field(&mut self, s: &'v hir::StructField<'v>) {
        self.record("StructField", Id::Node(s.hir_id), s);
        hir_visit::walk_struct_field(self, s)
    }

    fn visit_variant(
        &mut self,
        v: &'v hir::Variant<'v>,
        g: &'v hir::Generics<'v>,
        item_id: hir::HirId,
    ) {
        self.record("Variant", Id::None, v);
        hir_visit::walk_variant(self, v, g, item_id)
    }

    fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
        self.record("Lifetime", Id::Node(lifetime.hir_id), lifetime);
        hir_visit::walk_lifetime(self, lifetime)
    }

    fn visit_qpath(&mut self, qpath: &'v hir::QPath<'v>, id: hir::HirId, span: Span) {
        self.record("QPath", Id::None, qpath);
        hir_visit::walk_qpath(self, qpath, id, span)
    }

    fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) {
        self.record("Path", Id::None, path);
        hir_visit::walk_path(self, path)
    }

    fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v hir::PathSegment<'v>) {
        self.record("PathSegment", Id::None, path_segment);
        hir_visit::walk_path_segment(self, path_span, path_segment)
    }

    fn visit_assoc_type_binding(&mut self, type_binding: &'v hir::TypeBinding<'v>) {
        self.record("TypeBinding", Id::Node(type_binding.hir_id), type_binding);
        hir_visit::walk_assoc_type_binding(self, type_binding)
    }

    fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
        self.record("Attribute", Id::Attr(attr.id), attr);
    }

    fn visit_macro_def(&mut self, macro_def: &'v hir::MacroDef<'v>) {
        self.record("MacroDef", Id::Node(macro_def.hir_id()), macro_def);
        hir_visit::walk_macro_def(self, macro_def)
    }
}

impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
    fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
        self.record("ForeignItem", Id::None, i);
        ast_visit::walk_foreign_item(self, i)
    }

    fn visit_item(&mut self, i: &'v ast::Item) {
        self.record("Item", Id::None, i);
        ast_visit::walk_item(self, i)
    }

    fn visit_local(&mut self, l: &'v ast::Local) {
        self.record("Local", Id::None, l);
        ast_visit::walk_local(self, l)
    }

    fn visit_block(&mut self, b: &'v ast::Block) {
        self.record("Block", Id::None, b);
        ast_visit::walk_block(self, b)
    }

    fn visit_stmt(&mut self, s: &'v ast::Stmt) {
        self.record("Stmt", Id::None, s);
        ast_visit::walk_stmt(self, s)
    }

    fn visit_arm(&mut self, a: &'v ast::Arm) {
        self.record("Arm", Id::None, a);
        ast_visit::walk_arm(self, a)
    }

    fn visit_pat(&mut self, p: &'v ast::Pat) {
        self.record("Pat", Id::None, p);
        ast_visit::walk_pat(self, p)
    }

    fn visit_expr(&mut self, ex: &'v ast::Expr) {
        self.record("Expr", Id::None, ex);
        ast_visit::walk_expr(self, ex)
    }

    fn visit_ty(&mut self, t: &'v ast::Ty) {
        self.record("Ty", Id::None, t);
        ast_visit::walk_ty(self, t)
    }

    fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, s: Span, _: NodeId) {
        self.record("FnDecl", Id::None, fk.decl());
        ast_visit::walk_fn(self, fk, s)
    }

    fn visit_assoc_item(&mut self, item: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
        let label = match ctxt {
            ast_visit::AssocCtxt::Trait => "TraitItem",
            ast_visit::AssocCtxt::Impl => "ImplItem",
        };
        self.record(label, Id::None, item);
        ast_visit::walk_assoc_item(self, item, ctxt);
    }

    fn visit_param_bound(&mut self, bounds: &'v ast::GenericBound) {
        self.record("GenericBound", Id::None, bounds);
        ast_visit::walk_param_bound(self, bounds)
    }

    fn visit_struct_field(&mut self, s: &'v ast::StructField) {
        self.record("StructField", Id::None, s);
        ast_visit::walk_struct_field(self, s)
    }

    fn visit_variant(&mut self, v: &'v ast::Variant) {
        self.record("Variant", Id::None, v);
        ast_visit::walk_variant(self, v)
    }

    fn visit_lifetime(&mut self, lifetime: &'v ast::Lifetime) {
        self.record("Lifetime", Id::None, lifetime);
        ast_visit::walk_lifetime(self, lifetime)
    }

    fn visit_mac_call(&mut self, mac: &'v ast::MacCall) {
        self.record("MacCall", Id::None, mac);
        ast_visit::walk_mac(self, mac)
    }

    fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v ast::PathSegment) {
        self.record("PathSegment", Id::None, path_segment);
        ast_visit::walk_path_segment(self, path_span, path_segment)
    }

    fn visit_assoc_ty_constraint(&mut self, constraint: &'v ast::AssocTyConstraint) {
        self.record("AssocTyConstraint", Id::None, constraint);
        ast_visit::walk_assoc_ty_constraint(self, constraint)
    }

    fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
        self.record("Attribute", Id::None, attr);
    }
}
<|endoftext|>


<commit_before>#![feature(unboxed_closures)]
#![feature(globs)]
#![feature(phase)]
#![feature(macro_rules)]
#[phase(plugin, link)]
extern crate log;
#[phase(plugin)]
extern crate wtftw_core;

extern crate wtftw_core;

use wtftw_core::window_system::*;
use wtftw_core::window_manager::*;
use wtftw_core::handlers::default::*;
use wtftw_core::config::*;
use wtftw_core::util::*;
use wtftw_core::layout::*;
use wtftw_core::layout::Direction::*;
use wtftw_core::layout::LayoutMessage::*;

#[no_mangle]
pub extern fn configure(_: &mut WindowManager, w: &WindowSystem, config: &mut Config) {
    let modm = MOD1MASK;

    config.general.mod_mask = modm;
    config.general.spacing = 0;
    config.general.border_color = 0x3f3f4c;
    config.general.focus_border_color = 0x525263;
    config.general.terminal = (String::from_str("urxvt"), String::from_str(""));
    config.general.layout = LayoutCollection::new(vec!(
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up), ResizableTallLayout::new())),
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up),
MirrorLayout::new(ResizableTallLayout::new()))),
        box FullLayout));

    config.general.tags = (vec!("1: term", "2: web", "3: code",
                                "4: media", "5: steam", "6: latex",
                                "7: music", "8: im", "9: rest"))
        .into_iter().map(String::from_str).collect();

    // Register key handlers

    // Some standard key handlers for starting, restarting, etc.
    add_key_handler_str!(config, w, "q",      modm | SHIFTMASK, exit);
    add_key_handler_str!(config, w, "q",      modm,             restart);
    add_key_handler_str!(config, w, "Return", modm | SHIFTMASK, start_terminal);
    add_key_handler_str!(config, w, "p",      modm,             start_launcher);

    // Focus and window movement
    add_key_handler_str!(config, w, "j", modm, |&: m, w, c| m.windows(w, c, |x| x.focus_down()));
    add_key_handler_str!(config, w, "k", modm, |&: m, w, c| m.windows(w, c, |x| x.focus_up()));
    add_key_handler_str!(config, w, "j", modm | SHIFTMASK, |&: m, w, c| m.windows(w, c, |x|
x.swap_down()));
    add_key_handler_str!(config, w, "k", modm | SHIFTMASK, |&: m, w, c| m.windows(w, c, |x| x.swap_up()));
    add_key_handler_str!(config, w, "Return", modm, |&: m, w, c| m.windows(w, c, |x| x.swap_master()));

    // Layout messages
    add_key_handler_str!(config, w, "h", modm,
            |&: m, w, c| { m.send_layout_message(LayoutMessage::Decrease).reapply_layout(w, c) });
    add_key_handler_str!(config, w, "l", modm,
            |&: m, w, c| { m.send_layout_message(LayoutMessage::Increase).reapply_layout(w, c) });
    add_key_handler_str!(config, w, "comma", modm,
            |&: m, w, c| { m.send_layout_message(LayoutMessage::IncreaseMaster).reapply_layout(w, c) });
    add_key_handler_str!(config, w, "period", modm,
            |&: m, w, c| m.send_layout_message(LayoutMessage::DecreaseMaster).reapply_layout(w, c));
    add_key_handler_str!(config, w, "space", modm,
            |&: m, w, c| m.send_layout_message(LayoutMessage::Next).reapply_layout(w, c));

    // Workspace switching and moving
    for i in range(1u, 10) {
        add_key_handler_str!(config, w, i.to_string().as_slice(), modm,
            move |&: m, w, c| switch_to_workspace(m, w, c, i - 1));

        add_key_handler_str!(config, w, i.to_string().as_slice(), modm | SHIFTMASK,
            move |&: m, w, c| move_window_to_workspace(m, w, c, i - 1));
    }

    // Media keys
    add_key_handler_str!(config, w, "j", modm | CONTROLMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%-")); w });
    add_key_handler_str!(config, w, "k", modm | CONTROLMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%+")); w });

    add_key_handler_code!(config, 0x1008ff11, NONEMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%-")); w });
    add_key_handler_code!(config, 0x1008ff13, NONEMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%+")); w });

    add_key_handler_code!(config, 0x1008ff02, NONEMASK,
            |&: w, _, _| { run("xbacklight", Some("+10")); w });
    add_key_handler_code!(config, 0x1008ff03, NONEMASK,
            |&: w, _, _| { run("xbacklight", Some("-10")); w });

    // Place specific applications on specific workspaces
    config.set_manage_hook(box |&: workspaces, window_system, window| {
                match window_system.get_class_name(window).as_slice() {
                    "MPlayer" => spawn_on(workspaces, window_system, window, 3),
                    "vlc"     => spawn_on(workspaces, window_system, window, 3),
                    _         => workspaces.clone()
                }
            });

    // Xmobar handling and formatting
    let mut xmobar = spawn_pipe(config, String::from_str("xmobar"),
                                Some(String::from_str("/home/wollwage/.xmonad/xmobar1.hs")));
    let tags = config.general.tags.clone();
    config.set_log_hook(box move |&mut: m, _| {
        let p = &mut xmobar;
        let tags = &tags;
        let workspaces = tags.clone().iter()
            .enumerate()
            .map(|(i, x)| if i as u32 == m.workspaces.current.workspace.id {
                format!("[<fc=#3279a8>{}</fc>] ", x)
            } else {
                format!("[{}] ", x)
            })
            .fold(String::from_str(""), |a, x| {
                let mut r = a.clone();
                r.push_str(x.as_slice());
                r
            });

        let content = format!("{} {}", workspaces, m.workspaces.current.workspace.layout.description());
        match p.stdin.as_mut() {
            Some(pin) => match pin.write_line(content.as_slice()) {
                _ => ()
            },
            _ => ()
        }
    });
}

<commit_msg>updated default config<commit_after>#![feature(unboxed_closures)]
#![feature(globs)]
#![feature(phase)]
#![feature(macro_rules)]
#[phase(plugin, link)]
extern crate log;
#[phase(plugin)]
extern crate wtftw_core;

extern crate wtftw_core;

use wtftw_core::window_system::*;
use wtftw_core::window_manager::*;
use wtftw_core::handlers::default::*;
use wtftw_core::config::*;
use wtftw_core::util::*;
use wtftw_core::layout::*;
use wtftw_core::layout::Direction::*;
use wtftw_core::layout::LayoutMessage::*;

#[no_mangle]
pub extern fn configure(_: &mut WindowManager, w: &WindowSystem, config: &mut Config) {
    let modm = MOD1MASK;

    config.general.mod_mask = modm;
    config.general.spacing = 0;
    config.general.border_color = 0x3f3f4c;
    config.general.focus_border_color = 0x525263;
    config.general.terminal = (String::from_str("urxvt"), String::from_str(""));
    config.general.layout = LayoutCollection::new(vec!(
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up), ResizableTallLayout::new())),
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up),
MirrorLayout::new(ResizableTallLayout::new()))),
        box FullLayout));

    config.general.tags = (vec!("1: term", "2: web", "3: code",
                                "4: media", "5: steam", "6: latex",
                                "7: music", "8: im", "9: rest"))
        .into_iter().map(String::from_str).collect();

    // Register key handlers

    // Some standard key handlers for starting, restarting, etc.
    add_key_handler_str!(config, w, "q",      modm | SHIFTMASK, exit);
    add_key_handler_str!(config, w, "q",      modm,             restart);
    add_key_handler_str!(config, w, "Return", modm | SHIFTMASK, start_terminal);
    add_key_handler_str!(config, w, "p",      modm,             start_launcher);

    // Focus and window movement
    add_key_handler_str!(config, w, "j", modm, |&: m, w, c| m.windows(w, c, |x| x.focus_down()));
    add_key_handler_str!(config, w, "k", modm, |&: m, w, c| m.windows(w, c, |x| x.focus_up()));
    add_key_handler_str!(config, w, "j", modm | SHIFTMASK, |&: m, w, c| m.windows(w, c, |x|
x.swap_down()));
    add_key_handler_str!(config, w, "k", modm | SHIFTMASK, |&: m, w, c| m.windows(w, c, |x| x.swap_up()));
    add_key_handler_str!(config, w, "Return", modm, |&: m, w, c| m.windows(w, c, |x| x.swap_master()));

    // Layout messages
    add_key_handler_str!(config, w, "h", modm,
            |&: m, w, c| { m.send_layout_message(LayoutMessage::Decrease).reapply_layout(w, c) });
    add_key_handler_str!(config, w, "l", modm,
            |&: m, w, c| { m.send_layout_message(LayoutMessage::Increase).reapply_layout(w, c) });
    add_key_handler_str!(config, w, "comma", modm,
            |&: m, w, c| { m.send_layout_message(LayoutMessage::IncreaseMaster).reapply_layout(w, c) });
    add_key_handler_str!(config, w, "period", modm,
            |&: m, w, c| m.send_layout_message(LayoutMessage::DecreaseMaster).reapply_layout(w, c));
    add_key_handler_str!(config, w, "space", modm,
            |&: m, w, c| m.send_layout_message(LayoutMessage::Next).reapply_layout(w, c));

    // Workspace switching and moving
    for i in range(1u, 10) {
        add_key_handler_str!(config, w, i.to_string().as_slice(), modm,
            move |&: m, w, c| switch_to_workspace(m, w, c, i - 1));

        add_key_handler_str!(config, w, i.to_string().as_slice(), modm | SHIFTMASK,
            move |&: m, w, c| move_window_to_workspace(m, w, c, i - 1));
    }

    // Media keys
    add_key_handler_str!(config, w, "j", modm | CONTROLMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%-")); w });
    add_key_handler_str!(config, w, "k", modm | CONTROLMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%+")); w });

    add_key_handler_code!(config, 0x1008ff11, NONEMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%-")); w });
    add_key_handler_code!(config, 0x1008ff13, NONEMASK,
            |&: w, _, _| { run("amixer", Some("-q set Master 5%+")); w });

    add_key_handler_code!(config, 0x1008ff02, NONEMASK,
            |&: w, _, _| { run("xbacklight", Some("+10")); w });
    add_key_handler_code!(config, 0x1008ff03, NONEMASK,
            |&: w, _, _| { run("xbacklight", Some("-10")); w });

    // Place specific applications on specific workspaces
    config.set_manage_hook(box |&: workspaces, window_system, window| {
                match window_system.get_class_name(window).as_slice() {
                    "MPlayer" => spawn_on(workspaces, window_system, window, 3),
                    "vlc"     => spawn_on(workspaces, window_system, window, 3),
                    _         => workspaces.clone()
                }
            });

    // Xmobar handling and formatting
    let mut xmobar = spawn_pipe(config, String::from_str("xmobar"),
                                Some(String::from_str("/home/wollwage/.xmonad/xmobar1.hs")));
    let tags = config.general.tags.clone();
    config.set_log_hook(box move |&mut: m, _| {
        let p = &mut xmobar;
        let tags = &tags;
        let workspaces = tags.clone().iter()
            .enumerate()
            .map(|(i, x)| if i as u32 == m.workspaces.current.workspace.id {
                format!("[<fc=#3279a8>{}</fc>] ", x)
            } else {
                format!("[{}] ", x)
            })
            .fold(String::from_str(""), |a, x| {
                let mut r = a.clone();
                r.push_str(x.as_slice());
                r
            });

        let content = format!("{} {}", workspaces, m.workspaces.current.workspace.layout.description());
        match p.deref().write().deref_mut().stdin.as_mut() {
            Some(pin) => match pin.write_line(content.as_slice()) {
                _ => ()
            },
            _ => ()
        }
    });
}

<|endoftext|>


<commit_before>//! Contains information for keybindings.

use std::collections::{HashMap, HashSet};
use std::sync::{Arc, RwLock};
use rustwlc::xkb::{Keysym, NameFlags};
use rustwlc::types::*; // Need * for bitflags...
use std::hash::{Hash, Hasher};

lazy_static! {
    static ref BINDINGS: RwLock<HashMap<KeyPress, KeyEvent>> =
        RwLock::new(HashMap::new());
}

#[derive(Eq, PartialEq, Clone)]
pub struct KeyPress {
    modifiers: KeyMod,
    keys: Vec<Keysym>
}

/// Parses a KeyMod from key names.
pub fn keymod_from_names(keys: Vec<&str>) -> Result<KeyMod, String> {
    let mut result = KeyMod::empty();
    for key in keys {
        match key.to_lowercase().as_str() {
            "shift"            => result = result | MOD_SHIFT,
            "control" | "ctrl" => result = result | MOD_CTRL,
            "alt"              => result = result | MOD_ALT,
            "mod2"             => result = result | MOD_MOD2,
            "mod3"             => result = result | MOD_MOD3,
            "mod4" | "super" | "logo" => result = result | MOD_MOD4,
            "mod5" | "5mod5me" => result = result | MOD_MOD5,
            err => return Err(format!("Invalid modifier: {}", err))
        }
    }
    return Ok(result);
}

impl KeyPress {
    /// Creates a new KeyPress struct from a list of modifier and key names
    pub fn from_key_names(mods: Vec<&str>, keys: Vec<&str>) -> Result<KeyPress, String> {
        match keymod_from_names(mods) {
            Err(message) => Err(message),
            Ok(mods) => {
                let mut syms: Vec<Keysym> = Vec::with_capacity(keys.len());
                for name in keys {
                    if let Some(sym) = Keysym::from_name(name.to_string(),
                                                         NameFlags::None) {
                            syms.push(sym);
                    }
                    // Else if could not parse
                    else if let Some(sym) = Keysym::from_name(name.to_string(),
                                                        NameFlags::CaseInsensitive) {
                        syms.push(sym);
                    }
                    else {
                        return Err(format!("Invalid key: {}", name));
                    }
                }
                syms.sort_by_key(|s| s.get_code());
                return Ok(KeyPress { modifiers: mods, keys: syms });
            }
        }
    }
}

impl Hash for KeyPress {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        hasher.write_u32(self.modifiers.bits());
        for ref sym in &self.keys {
            hasher.write_u32(sym.get_code());
        }
    }
}

/// The type of function a key press handler is.
pub type KeyEvent = Arc<Box<FnOnce() + Send + Sync>>;

pub fn get(key: &KeyPress) -> Option<KeyEvent> {
    let bindings = BINDINGS.read().unwrap();
    match bindings.get(key) {
        None => None,
        Some(val) => Some(val.clone())
    }
}

pub fn register(values: Vec<(KeyPress, KeyEvent)>) {
    let mut bindings = BINDINGS.write().unwrap();
    for value in values {
        bindings.insert(value.0, value.1);
    }
}
<commit_msg>Add Keypres::new for use in key callback<commit_after>//! Contains information for keybindings.

use std::collections::{HashMap, HashSet};
use std::sync::{Arc, RwLock};
use rustwlc::xkb::{Keysym, NameFlags};
use rustwlc::types::*; // Need * for bitflags...
use std::hash::{Hash, Hasher};

lazy_static! {
    static ref BINDINGS: RwLock<HashMap<KeyPress, KeyEvent>> =
        RwLock::new(HashMap::new());
}

#[derive(Eq, PartialEq, Clone)]
pub struct KeyPress {
    modifiers: KeyMod,
    keys: Vec<Keysym>
}

/// Parses a KeyMod from key names.
pub fn keymod_from_names(keys: Vec<&str>) -> Result<KeyMod, String> {
    let mut result = KeyMod::empty();
    for key in keys {
        match key.to_lowercase().as_str() {
            "shift"            => result = result | MOD_SHIFT,
            "control" | "ctrl" => result = result | MOD_CTRL,
            "alt"              => result = result | MOD_ALT,
            "mod2"             => result = result | MOD_MOD2,
            "mod3"             => result = result | MOD_MOD3,
            "mod4" | "super" | "logo" => result = result | MOD_MOD4,
            "mod5" | "5mod5me" => result = result | MOD_MOD5,
            err => return Err(format!("Invalid modifier: {}", err))
        }
    }
    return Ok(result);
}

impl KeyPress {
    /// Creates a new KeyPress struct from a list of modifier and key names
    pub fn from_key_names(mods: Vec<&str>, keys: Vec<&str>) -> Result<KeyPress, String> {
        match keymod_from_names(mods) {
            Err(message) => Err(message),
            Ok(mods) => {
                let mut syms: Vec<Keysym> = Vec::with_capacity(keys.len());
                for name in keys {
                    if let Some(sym) = Keysym::from_name(name.to_string(),
                                                         NameFlags::None) {
                            syms.push(sym);
                    }
                    // Else if could not parse
                    else if let Some(sym) = Keysym::from_name(name.to_string(),
                                                        NameFlags::CaseInsensitive) {
                        syms.push(sym);
                    }
                    else {
                        return Err(format!("Invalid key: {}", name));
                    }
                }
                syms.sort_by_key(|s| s.get_code());
                syms.dedup();
                return Ok(KeyPress { modifiers: mods, keys: syms });
            }
        }
    }

    /// Creates a KeyPress from keys that are pressed at the moment
    pub fn new(mods: KeyMod, mut keys: Vec<Keysym>) -> KeyPress {
        keys.sort_by_key(|k| k.get_code());
        keys.dedup();

        KeyPress { modifiers: mods, keys: keys }
    }
}

impl Hash for KeyPress {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        hasher.write_u32(self.modifiers.bits());
        for ref sym in &self.keys {
            hasher.write_u32(sym.get_code());
        }
    }
}

/// The type of function a key press handler is.
pub type KeyEvent = Arc<Box<Fn() + Send + Sync>>;

/// Get a key mapping from the list.
pub fn get(key: &KeyPress) -> Option<KeyEvent> {
    let bindings = BINDINGS.read().unwrap();
    match bindings.get(key) {
        None => None,
        Some(val) => Some(val.clone())
    }
}

/// Register a new key mapping
pub fn register(values: Vec<(KeyPress, KeyEvent)>) {
    let mut bindings = BINDINGS.write().unwrap();
    for value in values {
        bindings.insert(value.0, value.1);
    }
}
<|endoftext|>


<commit_before>extern crate fallible_iterator;
extern crate gimli;
extern crate getopts;
extern crate memmap;
extern crate object;

use fallible_iterator::FallibleIterator;
use object::Object;
use std::env;
use std::fs;

fn main() {
    let args: Vec<String> = env::args().collect();
    let mut opts = getopts::Options::new();
    opts.optopt("e",
                "exe",
                "Set the input file name (default is a.out)",
                "<executable>");

    let matches = opts.parse(&args[1..]).unwrap();

    let file_path = matches.opt_str("e").unwrap_or("a.out".to_string());
    let file = fs::File::open(&file_path).expect("Should open file");
    let file = memmap::Mmap::open(&file, memmap::Protection::Read)
        .expect("Should create a mmap for file");
    let file = object::File::parse(unsafe { file.as_slice() });

    let addrs = matches.free.iter().map(|x| parse_uint_from_hex_string(x)).collect();

    if file.is_little_endian() {
        symbolicate::<gimli::LittleEndian>(&file, addrs);
    } else {
        symbolicate::<gimli::BigEndian>(&file, addrs);
    }
}

fn parse_uint_from_hex_string(string: &str) -> u64 {
    if string.len() > 2 && string.starts_with("0x") {
        u64::from_str_radix(&string[2..], 16).expect("Failed to parse address")
    } else {
        u64::from_str_radix(string, 16).expect("Failed to parse address")
    }
}

fn display_file<Endian>(header: &gimli::LineNumberProgramHeader<Endian>,
                        row: &gimli::LineNumberRow)
    where Endian: gimli::Endianity
{
    let file = row.file(header).unwrap();
    if let Some(directory) = file.directory(header) {
        println!("{}/{}:{}",
                 directory.to_string_lossy(),
                 file.path_name().to_string_lossy(),
                 row.line().unwrap());
    } else {
        println!("{}:{}",
                 file.path_name().to_string_lossy(),
                 row.line().unwrap());
    }
}

fn symbolicate<Endian>(file: &object::File, addrs: Vec<u64>)
    where Endian: gimli::Endianity
{
    let debug_info = file.get_section(".debug_info")
        .expect("Can't addr2line without .debug_info");
    let debug_info = gimli::DebugInfo::<Endian>::new(debug_info);
    let debug_abbrev = file.get_section(".debug_abbrev")
        .expect("Can't addr2line without .debug_abbrev");
    let debug_abbrev = gimli::DebugAbbrev::<Endian>::new(debug_abbrev);
    let debug_line = file.get_section(".debug_line")
        .expect("Can't addr2line without .debug_line");
    let debug_line = gimli::DebugLine::<Endian>::new(debug_line);
    let debug_ranges = file.get_section(".debug_ranges").unwrap_or(&[]);
    let debug_ranges = gimli::DebugRanges::<Endian>::new(debug_ranges);

    let mut units = Vec::new();
    let mut headers = debug_info.units();
    while let Some(header) = headers.next().expect("Couldn't get DIE header") {
        if let Some(unit) = Unit::parse(&debug_abbrev, &debug_ranges, &header) {
            units.push(unit);
        }
    }

    for addr in addrs {
        find_address(debug_line, &units, addr);
    }
}

fn find_address<Endian>(debug_line: gimli::DebugLine<Endian>, units: &[Unit], addr: u64)
    where Endian: gimli::Endianity
{
    let mut current = None;
    for unit in units {
        if unit.contains_address(addr) {
            if let Ok(mut lines) = unit.lines(debug_line) {
                while let Ok(Some((header, row))) = lines.next_row() {
                    if row.address() > addr {
                        if let Some(ref row) = current {
                            display_file(header, row);
                            return;
                        }
                        break;
                    }
                    if row.end_sequence() {
                        current = None;
                    } else {
                        current = Some(row.clone());
                    }
                }
            }
        }
    }
    println!("Failed to find matching line for {}", addr);
}

// TODO: most of this should be moved to the main library.
struct Unit {
    address_size: u8,
    ranges: Vec<gimli::Range>,
    line_offset: gimli::DebugLineOffset,
}

impl Unit {
    fn parse<Endian>(debug_abbrev: &gimli::DebugAbbrev<Endian>,
                     debug_ranges: &gimli::DebugRanges<Endian>,
                     header: &gimli::UnitHeader<Endian>)
                     -> Option<Unit>
        where Endian: gimli::Endianity
    {
        let abbrev = debug_abbrev.abbreviations(header.debug_abbrev_offset()).expect("Fail");
        let mut entries = header.entries(&abbrev);
        let (_, entry) = entries.next_dfs()
            .expect("Should parse first entry OK")
            .expect("And first entry should exist!");
        assert_eq!(entry.tag(), gimli::DW_TAG_compile_unit);

        let ranges = if let Some(ranges) =
                            Self::parse_noncontiguous_ranges(entry,
                                                             debug_ranges,
                                                             header.address_size()) {
            ranges
        } else if let Some(range) = Self::parse_contiguous_range(entry) {
            vec![range]
        } else {
            return None;
        };

        let line_offset = match entry.attr_value(gimli::DW_AT_stmt_list) {
            Some(gimli::AttributeValue::DebugLineRef(offset)) => offset,
            _ => return None,
        };

        Some(Unit {
            address_size: header.address_size(),
            ranges: ranges,
            line_offset: line_offset,
        })
    }

    // This must be checked before `parse_contiguous_range`.
    fn parse_noncontiguous_ranges<Endian>(entry: &gimli::DebuggingInformationEntry<Endian>,
                                          debug_ranges: &gimli::DebugRanges<Endian>,
                                          address_size: u8)
                                          -> Option<Vec<gimli::Range>>
        where Endian: gimli::Endianity
    {
        let offset = match entry.attr_value(gimli::DW_AT_ranges) {
            Some(gimli::AttributeValue::DebugRangesRef(offset)) => offset,
            _ => return None,
        };
        let base_address = match entry.attr_value(gimli::DW_AT_low_pc) {
            Some(gimli::AttributeValue::Addr(addr)) => addr,
            _ => 0,
        };
        let ranges = debug_ranges.ranges(offset, address_size, base_address)
            .expect("Range offset should be valid");
        Some(ranges.collect().expect("Should parse ranges"))
    }

    fn parse_contiguous_range<Endian>(entry: &gimli::DebuggingInformationEntry<Endian>)
                                      -> Option<gimli::Range>
        where Endian: gimli::Endianity
    {
        debug_assert!(entry.attr_value(gimli::DW_AT_ranges).is_none());

        let low_pc = match entry.attr_value(gimli::DW_AT_low_pc) {
            Some(gimli::AttributeValue::Addr(addr)) => addr,
            _ => return None,
        };

        let high_pc = match entry.attr_value(gimli::DW_AT_high_pc) {
            Some(gimli::AttributeValue::Addr(addr)) => addr,
            Some(gimli::AttributeValue::Udata(size)) => low_pc.wrapping_add(size),
            None => low_pc.wrapping_add(1),
            _ => return None,
        };

        // TODO: convert to error
        assert!(low_pc < high_pc);
        Some(gimli::Range {
            begin: low_pc,
            end: high_pc,
        })
    }

    fn contains_address(&self, address: u64) -> bool {
        self.ranges.iter().any(|range| address >= range.begin && address < range.end)
    }

    fn lines<'a, Endian>(&self,
                         debug_line: gimli::DebugLine<'a, Endian>)
                         -> gimli::Result<gimli::StateMachine<'a, Endian>>
        where Endian: gimli::Endianity
    {
        let header = try!(gimli::LineNumberProgramHeader::new(debug_line,
                                                              self.line_offset,
                                                              self.address_size));
        Ok(gimli::StateMachine::new(header))
    }
}
<commit_msg>addr2line: display compilation unit directory<commit_after>extern crate fallible_iterator;
extern crate gimli;
extern crate getopts;
extern crate memmap;
extern crate object;

use fallible_iterator::FallibleIterator;
use object::Object;
use std::env;
use std::fs;

fn main() {
    let args: Vec<String> = env::args().collect();
    let mut opts = getopts::Options::new();
    opts.optopt("e",
                "exe",
                "Set the input file name (default is a.out)",
                "<executable>");

    let matches = opts.parse(&args[1..]).unwrap();

    let file_path = matches.opt_str("e").unwrap_or("a.out".to_string());
    let file = fs::File::open(&file_path).expect("Should open file");
    let file = memmap::Mmap::open(&file, memmap::Protection::Read)
        .expect("Should create a mmap for file");
    let file = object::File::parse(unsafe { file.as_slice() });

    let addrs = matches.free.iter().map(|x| parse_uint_from_hex_string(x)).collect();

    if file.is_little_endian() {
        symbolicate::<gimli::LittleEndian>(&file, addrs);
    } else {
        symbolicate::<gimli::BigEndian>(&file, addrs);
    }
}

fn parse_uint_from_hex_string(string: &str) -> u64 {
    if string.len() > 2 && string.starts_with("0x") {
        u64::from_str_radix(&string[2..], 16).expect("Failed to parse address")
    } else {
        u64::from_str_radix(string, 16).expect("Failed to parse address")
    }
}

fn display_file<Endian>(unit: &Unit,
                        header: &gimli::LineNumberProgramHeader<Endian>,
                        row: &gimli::LineNumberRow)
    where Endian: gimli::Endianity
{
    let file = row.file(header).unwrap();
    if let Some(directory) = file.directory(header) {
        let directory = directory.to_string_lossy();
        if !directory.starts_with("/") {
            if let Some(comp_dir) = unit.comp_dir() {
                print!("{}/", comp_dir);
            }
        }
        println!("{}/{}:{}",
                 directory,
                 file.path_name().to_string_lossy(),
                 row.line().unwrap());
    } else {
        if let Some(comp_dir) = unit.comp_dir() {
            print!("{}/", comp_dir);
        }
        println!("{}:{}",
                 file.path_name().to_string_lossy(),
                 row.line().unwrap());
    }
}

fn symbolicate<Endian>(file: &object::File, addrs: Vec<u64>)
    where Endian: gimli::Endianity
{
    let debug_info = file.get_section(".debug_info")
        .expect("Can't addr2line without .debug_info");
    let debug_info = gimli::DebugInfo::<Endian>::new(debug_info);
    let debug_abbrev = file.get_section(".debug_abbrev")
        .expect("Can't addr2line without .debug_abbrev");
    let debug_abbrev = gimli::DebugAbbrev::<Endian>::new(debug_abbrev);
    let debug_line = file.get_section(".debug_line")
        .expect("Can't addr2line without .debug_line");
    let debug_line = gimli::DebugLine::<Endian>::new(debug_line);
    let debug_ranges = file.get_section(".debug_ranges").unwrap_or(&[]);
    let debug_ranges = gimli::DebugRanges::<Endian>::new(debug_ranges);
    let debug_str = file.get_section(".debug_str").unwrap_or(&[]);
    let debug_str = gimli::DebugStr::<Endian>::new(debug_str);

    let mut units = Vec::new();
    let mut headers = debug_info.units();
    while let Some(header) = headers.next().expect("Couldn't get DIE header") {
        if let Some(unit) = Unit::parse(&debug_abbrev, &debug_ranges, &debug_str, &header) {
            units.push(unit);
        }
    }

    for addr in addrs {
        find_address(debug_line, &units, addr);
    }
}

fn find_address<Endian>(debug_line: gimli::DebugLine<Endian>, units: &[Unit], addr: u64)
    where Endian: gimli::Endianity
{
    let mut current = None;
    for unit in units {
        if unit.contains_address(addr) {
            if let Ok(mut lines) = unit.lines(debug_line) {
                while let Ok(Some((header, row))) = lines.next_row() {
                    if row.address() > addr {
                        if let Some(ref row) = current {
                            display_file(unit, header, row);
                            return;
                        }
                        break;
                    }
                    if row.end_sequence() {
                        current = None;
                    } else {
                        current = Some(row.clone());
                    }
                }
            }
        }
    }
    println!("Failed to find matching line for {}", addr);
}

// TODO: most of this should be moved to the main library.
struct Unit {
    address_size: u8,
    ranges: Vec<gimli::Range>,
    line_offset: gimli::DebugLineOffset,
    comp_dir: Option<String>,
}

impl Unit {
    fn parse<Endian>(debug_abbrev: &gimli::DebugAbbrev<Endian>,
                     debug_ranges: &gimli::DebugRanges<Endian>,
                     debug_str: &gimli::DebugStr<Endian>,
                     header: &gimli::UnitHeader<Endian>)
                     -> Option<Unit>
        where Endian: gimli::Endianity
    {
        let abbrev = debug_abbrev.abbreviations(header.debug_abbrev_offset()).expect("Fail");
        let mut entries = header.entries(&abbrev);
        let (_, entry) = entries.next_dfs()
            .expect("Should parse first entry OK")
            .expect("And first entry should exist!");
        assert_eq!(entry.tag(), gimli::DW_TAG_compile_unit);

        let ranges = if let Some(ranges) =
                            Self::parse_noncontiguous_ranges(entry,
                                                             debug_ranges,
                                                             header.address_size()) {
            ranges
        } else if let Some(range) = Self::parse_contiguous_range(entry) {
            vec![range]
        } else {
            return None;
        };

        let line_offset = match entry.attr_value(gimli::DW_AT_stmt_list) {
            Some(gimli::AttributeValue::DebugLineRef(offset)) => offset,
            _ => return None,
        };

        let comp_dir = match entry.attr_value(gimli::DW_AT_comp_dir) {
                Some(gimli::AttributeValue::String(dir)) => Some(dir),
                Some(gimli::AttributeValue::DebugStrRef(offset)) => debug_str.get_str(offset).ok(),
                _ => None,
            }
            .map(|dir| dir.to_string_lossy().into_owned());

        Some(Unit {
            address_size: header.address_size(),
            ranges: ranges,
            line_offset: line_offset,
            comp_dir: comp_dir,
        })
    }

    // This must be checked before `parse_contiguous_range`.
    fn parse_noncontiguous_ranges<Endian>(entry: &gimli::DebuggingInformationEntry<Endian>,
                                          debug_ranges: &gimli::DebugRanges<Endian>,
                                          address_size: u8)
                                          -> Option<Vec<gimli::Range>>
        where Endian: gimli::Endianity
    {
        let offset = match entry.attr_value(gimli::DW_AT_ranges) {
            Some(gimli::AttributeValue::DebugRangesRef(offset)) => offset,
            _ => return None,
        };
        let base_address = match entry.attr_value(gimli::DW_AT_low_pc) {
            Some(gimli::AttributeValue::Addr(addr)) => addr,
            _ => 0,
        };
        let ranges = debug_ranges.ranges(offset, address_size, base_address)
            .expect("Range offset should be valid");
        Some(ranges.collect().expect("Should parse ranges"))
    }

    fn parse_contiguous_range<Endian>(entry: &gimli::DebuggingInformationEntry<Endian>)
                                      -> Option<gimli::Range>
        where Endian: gimli::Endianity
    {
        debug_assert!(entry.attr_value(gimli::DW_AT_ranges).is_none());

        let low_pc = match entry.attr_value(gimli::DW_AT_low_pc) {
            Some(gimli::AttributeValue::Addr(addr)) => addr,
            _ => return None,
        };

        let high_pc = match entry.attr_value(gimli::DW_AT_high_pc) {
            Some(gimli::AttributeValue::Addr(addr)) => addr,
            Some(gimli::AttributeValue::Udata(size)) => low_pc.wrapping_add(size),
            None => low_pc.wrapping_add(1),
            _ => return None,
        };

        // TODO: convert to error
        assert!(low_pc < high_pc);
        Some(gimli::Range {
            begin: low_pc,
            end: high_pc,
        })
    }

    fn contains_address(&self, address: u64) -> bool {
        self.ranges.iter().any(|range| address >= range.begin && address < range.end)
    }

    fn lines<'a, Endian>(&self,
                         debug_line: gimli::DebugLine<'a, Endian>)
                         -> gimli::Result<gimli::StateMachine<'a, Endian>>
        where Endian: gimli::Endianity
    {
        let header = try!(gimli::LineNumberProgramHeader::new(debug_line,
                                                              self.line_offset,
                                                              self.address_size));
        Ok(gimli::StateMachine::new(header))
    }

    fn comp_dir(&self) -> Option<&String> {
        self.comp_dir.as_ref()
    }
}
<|endoftext|>