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

use ast::Expression;
use table::Table;
use error::Error;
use to_javascript::ToJavaScript;

type Callback = fn(String, &mut [Box<Expression>], &mut Stdlib) -> Result<String, Error>;

type FunctionMap = HashMap<String, Callback>;

pub struct Stdlib<'a> {
    pub function_table: FunctionMap,
    pub variable_table: Table<'a, String>,
}

impl<'a> Stdlib<'a> {
    pub fn new(variable_table: Table<'a, String>) -> Stdlib<'a> {
        Stdlib {
            function_table: FunctionMap::new(),
            variable_table,
        }
    }

    pub fn populate(&mut self) {
        self.function_table.insert("if".to_string(), builtin_if);

        self.function_table.insert("return".to_string(), builtin_return);

        self.function_table.insert("const".to_string(), builtin_binding);

        self.function_table.insert("var".to_string(), builtin_binding);

        self.function_table.insert("let".to_string(), builtin_binding);

        // Plus and minus are both binary and unary
        // But I  have deemed binary to have a higher precedence, so binary goes first
        self.function_table.insert("+".to_string(), builtin_binop);

        self.function_table.insert("-".to_string(), builtin_binop);

        self.function_table.insert("*".to_string(), builtin_binop);

        self.function_table.insert("/".to_string(), builtin_binop);

        self.function_table.insert("%".to_string(), builtin_binop);

        self.function_table.insert("!".to_string(), builtin_unary);

        self.function_table.insert("++".to_string(), builtin_unary);

        self.function_table.insert("--".to_string(), builtin_unary);

        self.function_table.insert("~".to_string(), builtin_unary);

        self.function_table.insert("typeof".to_string(), builtin_unary);

        self.function_table.insert("delete".to_string(), builtin_unary);
    }
}

fn builtin_if(
    _name: String,
    args: &mut [Box<Expression>],
    stdlib: &mut Stdlib,
) -> Result<String, Error> {
    match args.len() {
        0 => Err(Error::too_few_arguments("if statement".to_string())),
        1 => Err(Error::too_few_arguments("if statement condition".to_string())),
        2 => Ok(format!(
            "if ({}) {{ {} }}",
            args[0].eval(stdlib)?,
            args[1].eval(stdlib)?
        )),
        3 => Ok(format!(
            "if ({}) {{ {} }} else {{ {} }}",
            args[0].eval(stdlib)?,
            args[1].eval(stdlib)?,
            args[2].eval(stdlib)?
        )),
        // TODO: Add error message here
        _ => panic!("Unknown number of arguments supplied to if statement"),
    }
}

fn builtin_return(
    _name: String,
    args: &mut [Box<Expression>],
    stdlib: &mut Stdlib,
) -> Result<String, Error> {
    match args.len() {
        0 => Err(Error::too_few_arguments("return".to_string())),
        1 => Ok(format!("return {}", args[0].eval(stdlib)?)),
        _ => Err(Error::too_many_arguments("return".to_string())),
    }
}

fn builtin_binding(
    name: String,
    args: &mut [Box<Expression>],
    stdlib: &mut Stdlib,
) -> Result<String, Error> {
    // TODO: Add name to the error messages
    match args.len() {
        0 | 1 => Err(Error::too_few_arguments("binding".to_string())),
        2 => {
            let (ident, value) = args.split_first_mut().unwrap();

            match ident {
                box Expression::Identifier(ref mut ident) => {
                    // TODO: Remove clones
                    stdlib.variable_table.insert(ident.value.clone(), ident.value.clone());

                    Ok(format!("{} {} = {}", name, ident.value.clone(), value[0].eval(stdlib)?))
                },

                _ => Err(Error::invalid_expression("non-identifier given to binding".to_string())),
            }
        },
        _ => Err(Error::too_many_arguments("binding".to_string())),
    }
}

fn builtin_binop(
    op: String,
    args: &mut [Box<Expression>],
    stdlib: &mut Stdlib,
) -> Result<String, Error> {
    match args.len() {
        0 => Err(Error::too_few_arguments("binary operation".to_string())),
        1 => builtin_unary(op, args, stdlib),
        2 => {
            // Debox and take from index
            // This is messy _but_ it should make the match easier to understand
            let box ref left = args[0];

            let box ref right = args[1];

            match (left, right) {
                (Expression::Number(l), Expression::Number(r)) => precalculate_numbers(op, l.value, r.value),

                // TODO: Fix this
                _ => unimplemented!(),
                //(_, _) => Ok(format!("{}{}{}", left.eval(stdlib)?, op, right.eval(stdlib)?)),
            }
        }
        _ => {
            let joined = args
                .into_iter()

                // TODO: Remove unwrap
                .map(|expr| expr.eval(stdlib).or_else(|e| Err(e)).unwrap())
                .collect::<Vec<String>>()
                .join(&op);

            Ok(joined)
        }
    }
}

fn builtin_unary(
    op: String,
    args: &mut [Box<Expression>],
    stdlib: &mut Stdlib,
) -> Result<String, Error> {
    match op.as_ref() {
        "+" | "-" | "!" | "++" | "--" | "~" => Ok(format!("{}{}", op, args[0].eval(stdlib)?)),

        // Unary operators which are words next an extra space.
        "typeof" | "delete" => Ok(format!("{} {}", op, args[0].eval(stdlib)?)),
        _ => Err(Error::too_few_arguments("unary operator".to_string())),
    }
}

fn precalculate_numbers(op: String, left: f64, right: f64) -> Result<String, Error> {
    match op.as_ref() {
        "+" => Ok(format!("{}", left + right)),
        "-" => Ok(format!("{}", left - right)),
        "*" => Ok(format!("{}", left * right)),
        "/" if right != 0.0 => Ok(format!("{}", left / right)),
        "%" => Ok(format!("{}", left % right)),

        "/" => Err(Error::invalid_expression(
            "Divide by zero encountered on numeric literal binary operation".to_string(),
        )),

        // Assume divide by 0 here
        _ => Err(Error::invalid_expression(
            "Divide by zero encountered on numeric literal binary operation".to_string(),
        )),
    }
}
<commit_msg>Fix binop function<commit_after>use std::collections::HashMap;

use ast::Expression;
use table::Table;
use error::Error;
use to_javascript::ToJavaScript;

type Callback = fn(String, &mut [Box<Expression>], &mut Stdlib) -> Result<String, Error>;

type FunctionMap = HashMap<String, Callback>;

pub struct Stdlib<'a> {
    pub function_table: FunctionMap,
    pub variable_table: Table<'a, String>,
}

impl<'a> Stdlib<'a> {
    pub fn new(variable_table: Table<'a, String>) -> Stdlib<'a> {
        Stdlib {
            function_table: FunctionMap::new(),
            variable_table,
        }
    }

    pub fn populate(&mut self) {
        self.function_table.insert("if".to_string(), builtin_if);

        self.function_table.insert("return".to_string(), builtin_return);

        self.function_table.insert("const".to_string(), builtin_binding);

        self.function_table.insert("var".to_string(), builtin_binding);

        self.function_table.insert("let".to_string(), builtin_binding);

        // Plus and minus are both binary and unary
        // But I  have deemed binary to have a higher precedence, so binary goes first
        self.function_table.insert("+".to_string(), builtin_binop);

        self.function_table.insert("-".to_string(), builtin_binop);

        self.function_table.insert("*".to_string(), builtin_binop);

        self.function_table.insert("/".to_string(), builtin_binop);

        self.function_table.insert("%".to_string(), builtin_binop);

        self.function_table.insert("!".to_string(), builtin_unary);

        self.function_table.insert("++".to_string(), builtin_unary);

        self.function_table.insert("--".to_string(), builtin_unary);

        self.function_table.insert("~".to_string(), builtin_unary);

        self.function_table.insert("typeof".to_string(), builtin_unary);

        self.function_table.insert("delete".to_string(), builtin_unary);
    }
}

fn builtin_if(
    _name: String,
    args: &mut [Box<Expression>],
    stdlib: &mut Stdlib,
) -> Result<String, Error> {
    match args.len() {
        0 => Err(Error::too_few_arguments("if statement".to_string())),
        1 => Err(Error::too_few_arguments("if statement condition".to_string())),
        2 => Ok(format!(
            "if ({}) {{ {} }}",
            args[0].eval(stdlib)?,
            args[1].eval(stdlib)?
        )),
        3 => Ok(format!(
            "if ({}) {{ {} }} else {{ {} }}",
            args[0].eval(stdlib)?,
            args[1].eval(stdlib)?,
            args[2].eval(stdlib)?
        )),
        // TODO: Add error message here
        _ => panic!("Unknown number of arguments supplied to if statement"),
    }
}

fn builtin_return(
    _name: String,
    args: &mut [Box<Expression>],
    stdlib: &mut Stdlib,
) -> Result<String, Error> {
    match args.len() {
        0 => Err(Error::too_few_arguments("return".to_string())),
        1 => Ok(format!("return {}", args[0].eval(stdlib)?)),
        _ => Err(Error::too_many_arguments("return".to_string())),
    }
}

fn builtin_binding(
    name: String,
    args: &mut [Box<Expression>],
    stdlib: &mut Stdlib,
) -> Result<String, Error> {
    // TODO: Add name to the error messages
    match args.len() {
        0 | 1 => Err(Error::too_few_arguments("binding".to_string())),
        2 => {
            let (ident, value) = args.split_first_mut().unwrap();

            match ident {
                box Expression::Identifier(ref mut ident) => {
                    // TODO: Remove clones
                    stdlib.variable_table.insert(ident.value.clone(), ident.value.clone());

                    Ok(format!("{} {} = {}", name, ident.value.clone(), value[0].eval(stdlib)?))
                },

                _ => Err(Error::invalid_expression("non-identifier given to binding".to_string())),
            }
        },
        _ => Err(Error::too_many_arguments("binding".to_string())),
    }
}

fn builtin_binop(
    op: String,
    args: &mut [Box<Expression>],
    stdlib: &mut Stdlib,
) -> Result<String, Error> {
    match args.len() {
        0 => Err(Error::too_few_arguments("binary operation".to_string())),
        1 => builtin_unary(op, args, stdlib),
        2 => {
            // This is messy _but_ it should make the match easier to understand
            match (&args[0], &args[1]) {
                (box Expression::Number(l), box Expression::Number(r)) => precalculate_numbers(op, l.value, r.value),

                // TODO: Fix this
                (_, _) => Ok(format!("{}{}{}", args[0].eval(stdlib)?, op, args[1].eval(stdlib)?)),
            }
        }
        _ => {
            let joined = args
                .into_iter()

                // TODO: Remove unwrap
                .map(|expr| expr.eval(stdlib).or_else(|e| Err(e)).unwrap())
                .collect::<Vec<String>>()
                .join(&op);

            Ok(joined)
        }
    }
}

fn builtin_unary(
    op: String,
    args: &mut [Box<Expression>],
    stdlib: &mut Stdlib,
) -> Result<String, Error> {
    match op.as_ref() {
        "+" | "-" | "!" | "++" | "--" | "~" => Ok(format!("{}{}", op, args[0].eval(stdlib)?)),

        // Unary operators which are words next an extra space.
        "typeof" | "delete" => Ok(format!("{} {}", op, args[0].eval(stdlib)?)),
        _ => Err(Error::too_few_arguments("unary operator".to_string())),
    }
}

fn precalculate_numbers(op: String, left: f64, right: f64) -> Result<String, Error> {
    match op.as_ref() {
        "+" => Ok(format!("{}", left + right)),
        "-" => Ok(format!("{}", left - right)),
        "*" => Ok(format!("{}", left * right)),
        "/" if right != 0.0 => Ok(format!("{}", left / right)),
        "%" => Ok(format!("{}", left % right)),

        "/" => Err(Error::invalid_expression(
            "Divide by zero encountered on numeric literal binary operation".to_string(),
        )),

        // Assume divide by 0 here
        _ => Err(Error::invalid_expression(
            "Divide by zero encountered on numeric literal binary operation".to_string(),
        )),
    }
}
<|endoftext|>


<commit_before>use core::fmt::Write;
use log::{Level, Metadata, Record};

struct PortE9;

/// Allow formatting
impl ::core::fmt::Write for PortE9 {
    fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
        unsafe {
            let mut port = cpuio::UnsafePort::<u8>::new(0xe9);
            for byte in s.bytes() {
                if byte == b'\n' {
                    port.write(b'\r');
                }
                port.write(byte);
            }
        }
        Ok(()) // Success. Always.
    }
}

static mut PORT_E9: PortE9 = PortE9;

macro_rules! e9_print {
    ($fmt:expr, $($arg:tt)*) => (
        unsafe {
            PORT_E9.write_fmt(format_args!(concat!($fmt, "\n"), $($arg)*)).unwrap()
        }
    );
}

struct SystemLogger;

const LEVEL_SCREEN: log::Level = log::Level::Info;
const LEVEL_PORTE9: log::Level = log::Level::Trace;

impl log::Log for SystemLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= LEVEL_SCREEN || metadata.level() <= LEVEL_PORTE9
    }

    fn log(&self, record: &Record) {
        let level = record.metadata().level();
        if level <= LEVEL_PORTE9 {
            e9_print!(
                "{:5} {} - {}",
                record.level(),
                record.target(),
                record.args()
            );
        }
        if level <= LEVEL_SCREEN {
            rprintln!(
                "{:5} {} - {}",
                record.level(),
                record.target(),
                record.args()
            );
        }
    }

    fn flush(&self) {}
}

static LOGGER: SystemLogger = SystemLogger;

pub fn enable() {
    log::set_logger(&LOGGER).unwrap();
    log::set_max_level(log::LevelFilter::Trace);
}
<commit_msg>Add separate log levels to screen and e9 port<commit_after>use core::fmt::Write;
use log::{Level, Metadata, Record};

struct PortE9;

/// Allow formatting
impl ::core::fmt::Write for PortE9 {
    fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
        unsafe {
            let mut port = cpuio::UnsafePort::<u8>::new(0xe9);
            for byte in s.bytes() {
                if byte == b'\n' {
                    port.write(b'\r');
                }
                port.write(byte);
            }
        }
        Ok(()) // Success. Always.
    }
}

static mut PORT_E9: PortE9 = PortE9;

macro_rules! e9_print {
    ($fmt:expr, $($arg:tt)*) => (
        unsafe {
            PORT_E9.write_fmt(format_args!(concat!($fmt, "\n"), $($arg)*)).unwrap()
        }
    );
}

struct SystemLogger;

pub const LEVEL_SCREEN: log::Level = log::Level::Info;
pub const LEVEL_PORTE9: log::Level = log::Level::Trace;

impl log::Log for SystemLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= LEVEL_SCREEN || metadata.level() <= LEVEL_PORTE9
    }

    fn log(&self, record: &Record) {
        let level = record.metadata().level();
        if level <= LEVEL_PORTE9 {
            e9_print!(
                "{:40} {:5}  {}",
                record.target(),
                record.level(),
                record.args()
            );
        }
        if level <= LEVEL_SCREEN {
            unsafe {
                rforce_unlock!();
            }
            rprintln!(
                "{:5} {} - {}",
                record.level(),
                record.target(),
                record.args()
            );
        }
    }

    fn flush(&self) {}
}

static LOGGER: SystemLogger = SystemLogger;

pub fn enable() {
    log::set_logger(&LOGGER).unwrap();
    log::set_max_level(log::LevelFilter::Trace);
}
<|endoftext|>


<commit_before>extern crate nix;
use nix::sys::ioctl::libc::pid_t;
type word_t = i32;
use nix::sys::ioctl::libc::size_t;
use std::ffi::OsStr;
use std::cell::Cell;

/*
struct Binding {
    host: OsStr,
    guest: OsStr,
    need_substitution: bool,
    must_exist: bool
}

struct FileSystemNameSpaceBindings {
    /// List of bindings as specified by the user but not canonicalized yet.
    pending: Vec<Binding>,
    /// List of bindings canonicalized and sorted in the "guest" order.
    guest: Vec<Binding>,
    /// List of bindings canonicalized and sorted in the "host" order.
    host: Vec<Binding>
}

impl FileSystemNameSpaceBindings {
    pub fn new() -> FileSystemNameSpaceBindings {
        FileSystemNameSpaceBindings {
            pending: vec![],
            guest: vec![],
            host: vec![]
        }
    }
}

/// Information related to a file-system name-space.
struct FileSystemNameSpace {
    bindings: FileSystemNameSpaceBindings,
    /// Current working directory, à la /proc/self/pwd.
    cwd: Cell<Option<OsStr>>
}

impl FileSystemNameSpace {
    pub fn new() -> FileSystemNameSpace {
        FileSystemNameSpace {
            bindings: FileSystemNameSpaceBindings::new(),
            cwd: Cell::new(None)
        }
    }
}

/// Virtual heap, emulated with a regular memory mapping.
struct Heap {
    base: Cell<Option<word_t>>,
    size: Cell<Option<size_t>>,
    prealloc_size: Cell<Option<size_t>>
}

impl Heap {
    pub fn new() -> Heap {
        Heap {
            base: Cell::new(None),
            size: Cell::new(None),
            prealloc_size: Cell::new(None)
        }
    }
}
*/

#[derive(Debug)]
pub struct Tracee {
    /// Process identifier.
    pid: pid_t,
    /*
    /// Is it running or not?
    running: bool,
    /// Is this tracee ready to be freed?
    terminated: bool,
    /// Parent of this tracee.
    parent: Cell<Option<Tracee>>,
    /// Is it a "clone", i.e has the same parent as its creator.
    clone: bool,
    // as_ptracer,
    // as_ptracee,
    /// Current status:
    ///       0: enter syscall
    ///       1: exit syscall no error
    ///  -errno: exit syscall with error.
    status: i32,
    // reconf,
    // chain,
    // load_info,
    /// Verbose level.
    verbose: i32,
    // seccomp
    /// Ensure the sysexit stage is always hit under seccomp.
    sys_exit_pending: bool,
    /// Information related to a file-system name-space.
    fs: Box<FileSystemNameSpace>,
    /// Virtual heap, emulated with a regular memory mapping.
    heap: Box<Heap>,
    /// Path to the executable, à la /proc/self/exe.
    exe: Cell<Option<OsStr>>,
    new_exe: Cell<Option<OsStr>>,
    // qemu: OsStr,
    // glue: OsStr,
    // extensions,
    // host_ldso_paths,
    // guest_ldso_paths,
    // tool_name,
    */
}

impl Tracee {

    pub fn new(pid: pid_t) -> Tracee {
        Tracee {
            pid: pid,
            /*
            running: false,
            terminated: false,
            parent: Cell::new(None),
            clone: false,
            status: 0,
            verbose: 0,
            sys_exit_pending: false,
            fs: FileSystemNameSpace::new(),
            heap: Heap::new(),
            exe: Cell::new(None),
            new_exe: Cell::new(None),
            */
        }
    }

    /*
     * UTILITIES
     */

    /*
    pub fn is_in_sys_enter(&self) -> bool {
        self.status == 0
    }

    pub fn is_in_sys_exit(&self) -> bool {
        !self.is_in_sys_enter()
    }
    */

    //pub fn is_in_sys_exit2(&self) -> bool {
    //    self.is_in_sys_exit() && get_sysnum((tracee), ORIGINAL) == sysnum)
    //}
}<commit_msg>Added skeletons of FileSystemNameSpace and Heap.<commit_after>extern crate nix;
use nix::sys::ioctl::libc::pid_t;
type word_t = i32;
use nix::sys::ioctl::libc::size_t;
use std::path::PathBuf;
use std::cell::Cell;

#[derive(Debug)]
struct Binding {
    host: PathBuf,
    guest: PathBuf,
    need_substitution: bool,
    must_exist: bool
}

#[derive(Debug)]
struct FileSystemNameSpaceBindings {
    /// List of bindings as specified by the user but not canonicalized yet.
    pending: Vec<Binding>,
    /// List of bindings canonicalized and sorted in the "guest" order.
    guest: Vec<Binding>,
    /// List of bindings canonicalized and sorted in the "host" order.
    host: Vec<Binding>
}

impl FileSystemNameSpaceBindings {
    pub fn new() -> FileSystemNameSpaceBindings {
        FileSystemNameSpaceBindings {
            pending: vec![],
            guest: vec![],
            host: vec![]
        }
    }
}

/// Information related to a file-system name-space.
#[derive(Debug)]
struct FileSystemNameSpace {
    bindings: FileSystemNameSpaceBindings,
    /// Current working directory, à la /proc/self/pwd.
    cwd: Option<PathBuf>
}

impl FileSystemNameSpace {
    pub fn new() -> FileSystemNameSpace {
        FileSystemNameSpace {
            bindings: FileSystemNameSpaceBindings::new(),
            cwd: None
        }
    }
}

/// Virtual heap, emulated with a regular memory mapping.
#[derive(Debug)]
struct Heap {
    base: Cell<Option<word_t>>,
    size: Cell<Option<size_t>>,
    prealloc_size: Cell<Option<size_t>>
}

impl Heap {
    pub fn new() -> Heap {
        Heap {
            base: Cell::new(None),
            size: Cell::new(None),
            prealloc_size: Cell::new(None)
        }
    }
}

#[derive(Debug)]
pub struct Tracee {
    /// Process identifier.
    pid: pid_t,
    /*
    /// Is it running or not?
    running: bool,
    /// Is this tracee ready to be freed?
    terminated: bool,
    /// Parent of this tracee.
    parent: Cell<Option<Tracee>>,
    /// Is it a "clone", i.e has the same parent as its creator.
    clone: bool,
    // as_ptracer,
    // as_ptracee,
    /// Current status:
    ///       0: enter syscall
    ///       1: exit syscall no error
    ///  -errno: exit syscall with error.
    status: i32,
    // reconf,
    // chain,
    // load_info,
    /// Verbose level.
    verbose: i32,
    // seccomp
    /// Ensure the sysexit stage is always hit under seccomp.
    sys_exit_pending: bool,
    */
    /// Information related to a file-system name-space.
    fs: Box<FileSystemNameSpace>,
    /// Virtual heap, emulated with a regular memory mapping.
    heap: Box<Heap>,
    /*
    /// Path to the executable, à la /proc/self/exe.
    exe: Cell<Option<OsStr>>,
    new_exe: Cell<Option<OsStr>>,
    // qemu: OsStr,
    // glue: OsStr,
    // extensions,
    // host_ldso_paths,
    // guest_ldso_paths,
    // tool_name,
    */
}

impl Tracee {

    pub fn new(pid: pid_t) -> Tracee {
        Tracee {
            pid: pid,
            heap: Box::new(Heap::new()),
            fs: Box::new(FileSystemNameSpace::new()),
            /*
            running: false,
            terminated: false,
            parent: Cell::new(None),
            clone: false,
            status: 0,
            verbose: 0,
            sys_exit_pending: false,
            exe: Cell::new(None),
            new_exe: Cell::new(None),
            */
        }
    }

    /*
     * UTILITIES
     */

    /*
    pub fn is_in_sys_enter(&self) -> bool {
        self.status == 0
    }

    pub fn is_in_sys_exit(&self) -> bool {
        !self.is_in_sys_enter()
    }
    */

    //pub fn is_in_sys_exit2(&self) -> bool {
    //    self.is_in_sys_exit() && get_sysnum((tracee), ORIGINAL) == sysnum)
    //}
}<|endoftext|>


<commit_before>// Farbfeld filter that applies a cool blurring effect over images
// Usage: png2ff < greyscale.png | blur | ff2png > false-colored.png

use std::io;
use std::io::prelude::*;
use std::io::BufReader;
use std::io::BufWriter;

extern crate byteorder;
use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};

extern crate rayon;
use rayon::prelude::*;

#[derive (Clone, Copy)]
struct FfPixel {
	pub r: u16,
	pub g: u16,
	pub b: u16,
	pub a: u16,
}

#[derive (Clone, Copy, Debug, PartialEq)]
struct PlaneCoord {
	pub x: i32,
	pub y: i32,
}

impl PlaneCoord {
	pub fn contains_x (&self, x: i32) -> bool {
		x >= 0 && x < self.x
	}

	pub fn contains_y (&self, y: i32) -> bool {
		y >= 0 && y < self.y
	}

	pub fn contains (&self, x: i32, y: i32) -> bool {
		self.contains_x (x) &&
		self.contains_y (y)
	}

	pub fn clamp_x (&self, x: i32) -> i32 {
		return if x < 0 {
			0
		}
		else if x >= self.x {
			self.x - 1
		}
		else {
			x
		}
	}

	pub fn clamp_y (&self, y: i32) -> i32 {
		return if y < 0 {
			0
		}
		else if y >= self.y {
			self.y - 1
		}
		else {
			y
		}
	}

	pub fn clamp (&self, pc: &PlaneCoord) -> PlaneCoord {
		PlaneCoord {
			x: self.clamp_x (pc.x),
			y: self.clamp_y (pc.y),
		}
	}

	pub fn index (&self, pc: &PlaneCoord) -> i32 {
		let pc = self.clamp (pc);

		pc.y * self.x + pc.x
	}

	pub fn clamped_index (&self, pc: &PlaneCoord) -> i32 {
		self.index (&self.clamp (pc))
	}
}

struct HdrPlane {
	pub size: PlaneCoord,
	pub pixels: Vec <f64>,
}

impl HdrPlane {
	pub fn new (size: PlaneCoord) -> HdrPlane {
		HdrPlane {
			size: size,
			pixels: vec! [0.0; (size.x * size.y) as usize],
		}
	}
}

fn read_pixel <T> (reader: &mut T) -> FfPixel where T: ReadBytesExt {
	FfPixel {
		r: reader.read_u16::<BigEndian> ().unwrap (),
		g: reader.read_u16::<BigEndian> ().unwrap (),
		b: reader.read_u16::<BigEndian> ().unwrap (),
		a: reader.read_u16::<BigEndian> ().unwrap (),
	}
}

fn write_pixel <T> (writer: &mut T, pix: &FfPixel) where T: WriteBytesExt {
	writer.write_u16::<BigEndian> (pix.r).unwrap ();
	writer.write_u16::<BigEndian> (pix.g).unwrap ();
	writer.write_u16::<BigEndian> (pix.b).unwrap ();
	writer.write_u16::<BigEndian> (pix.a).unwrap ();
}

fn pascal (n: u32) -> Vec <u64> {
	match n {
		0 => {
			vec! []
		}
		1 => {
			vec! [1]
		},
		2 => {
			vec! [1, 1]
		},
		3 => {
			vec! [1, 2, 1]
		},
		4 => {
			vec! [1, 3, 3, 1]
		}
		5 => {
			vec! [1, 4, 6, 4, 1]
		},
		_ => {
			let step = 5;
			let prev_row = pascal (n - step + 1);
			let step_row = pascal (step);

			let mut output = vec! [];
			for _ in 0..n {
				output.push (0);
			}

			for a in 0..prev_row.len () {
				for b in 0..step_row.len () {
					output [a + b] += prev_row [a] * step_row [b];
				}
			}

			output
		}
	}
}

struct ScanlineJob <'a> {
	pub y: i32,
	pub row_chunk: &'a mut [f64],
}

fn blur_hor (input_plane: &HdrPlane, filter: &Vec <u64>) -> HdrPlane {
	let offset = -(filter.len () as i32 - 1) / 2;
	let filter_sum: u64 = filter.iter ().sum ();
	let filter_f: Vec <f64> = filter.iter ().map (|x| *x as f64 / filter_sum as f64).collect ();

	let sz = input_plane.size;

	let mut pixels = vec! [0.0f64; (sz.x * sz.y) as usize];

	(0..sz.y).zip (pixels.chunks_mut (sz.x as usize)).map (|(y, row_chunk)| ScanlineJob { y: y, row_chunk: row_chunk }).collect::<Vec <ScanlineJob>> ().par_iter_mut ().for_each (|job| {
		let scanline_index = job.y * sz.x;

		for x in 0..sz.x {
			let offset_2 = x + offset;

			job.row_chunk [x as usize] = (sz.clamp_x (x + 0 + offset) - offset_2..sz.clamp_x (x + filter_f.len () as i32 + offset) - offset_2).map (|i| filter_f [i as usize] * input_plane.pixels [(scanline_index + i + offset_2) as usize]).sum ();
		}
	});

	HdrPlane {
		size: sz,
		pixels: pixels,
	}
}

fn blur_vert (input_plane: &HdrPlane, filter: &Vec <u64>) -> HdrPlane {
	let offset = -(filter.len () as i32 - 1) / 2;
	let filter_sum: u64 = filter.iter ().sum ();
	let filter_f: Vec <f64> = filter.iter ().map (|x| *x as f64 / filter_sum as f64).collect ();

	let sz = input_plane.size;

	let mut pixels = vec! [0.0f64; (sz.x * sz.y) as usize];

	(0..sz.y).zip (pixels.chunks_mut (sz.x as usize)).map (|(y, row_chunk)| ScanlineJob { y: y, row_chunk: row_chunk }).collect::<Vec <ScanlineJob>> ().par_iter_mut ().for_each (|job| {
		for x in 0..sz.x {
			let offset_2 = job.y + offset;

			job.row_chunk [x as usize] = (sz.clamp_y (job.y + 0 + offset) - offset_2..sz.clamp_y (job.y + filter_f.len () as i32 + offset) - offset_2).map (|i| filter_f [i as usize] * input_plane.pixels [( sz.index (&PlaneCoord {x: x, y: i + offset_2})) as usize]).sum ();
		}
	});

	HdrPlane {
		size: sz,
		pixels: pixels,
	}
}

fn read_farbfeld_grey <T> (reader: &mut T) -> HdrPlane where T: ReadBytesExt {
	let mut magic = [0; 8];
	reader.read (&mut magic [..]).unwrap ();

	// TODO: Assert that magic == 'farbfeld'
	//assert_eq! (magic, &"farbfeld");

	let width = reader.read_u32::<BigEndian>().unwrap() as i32;
	let height = reader.read_u32::<BigEndian>().unwrap() as i32;

	writeln!(&mut io::stderr(), "{} x {}", width, height).expect("failed printing to stderr");

	let mut pixels = vec![0.0; (width * height) as usize];

	for i in 0 .. width * height {
		let gamma = read_pixel (reader).g as f64 / 65536.0;
		pixels [i as usize] = gamma * gamma;
	}

	HdrPlane {
		size: PlaneCoord {
			x: width,
			y: height,
		},
		pixels: pixels,
	}
}

fn write_farbfeld_plane <T> (writer: &mut T, plane: &HdrPlane) where T: WriteBytesExt
{
	write_farbfeld (writer, &plane.size, plane.pixels.iter ().map (|x| {
		let val = (x.sqrt ().min (1.0) * 65535.0) as u16;

		FfPixel {
			r: val,
			g: val,
			b: val,
			a: 65535,
		}
	}));
}

fn double_to_ff (val: f64) -> u16 {
	(val.sqrt ().min (1.0) * 65535.0) as u16
}

fn write_farbfeld <T, I> (writer: &mut T, size: &PlaneCoord, pixels: I ) where T: WriteBytesExt, I: Iterator <Item = FfPixel>
{
	writer.write ("farbfeld".as_bytes ()).expect ("printing farbfeld");

	writer.write_u32::<BigEndian> (size.x as u32).unwrap ();
	writer.write_u32::<BigEndian> (size.y as u32).unwrap ();

	for pixel in pixels.take ((size.x * size.y) as usize) {
		write_pixel (writer, &pixel);
	}
}

fn blur (input_plane: &HdrPlane, filter: &Vec <u64>) -> HdrPlane {
	blur_vert (&blur_hor (input_plane, filter), filter)
	//blur_hor (input_plane, filter)
	//blur_vert (input_plane, filter)
}

fn blur_n (input_plane: &HdrPlane, filter: &Vec <u64>, n: u32) -> HdrPlane {
	match n {
		0 => {
			// I know this is dumb
			panic! ("Can't blur an image 0 times")
		},
		1 => {
			blur (input_plane, filter)
		},
		_ => {
			blur (&blur_n (input_plane, filter, n - 1), filter)
		},
	}
}

fn main () {
	// Reader
	let mut reader = BufReader::new (io::stdin ());

	let filter = pascal (41);

	let input_plane = read_farbfeld_grey (&mut reader);
	let blurry_1 = blur_n (&input_plane, &filter, 1);
	let blurry_10 = blur_n (&blurry_1, &filter, 9);
	let blurry_20 = blur_n (&blurry_10, &filter, 10);

	let sum_iter = (0..(input_plane.size.x * input_plane.size.y) as usize).map (|i| {
		let r = input_plane.pixels [i];
		let g = r;
		let b = r;

		let b = b + blurry_1.pixels [i] * 256.0 / 256.0;
		let g = g + blurry_1.pixels [i] * 128.0 / 256.0;

		let b = b + blurry_10.pixels [i] * 512.0 / 256.0;
		let g = g + blurry_10.pixels [i] * 128.0 / 256.0;

		let r = r + blurry_20.pixels [i] * 90.0 / 256.0;
		let b = b + blurry_20.pixels [i] * 30.0 / 256.0;

		FfPixel {
			r: double_to_ff (r),
			g: double_to_ff (g),
			b: double_to_ff (b),
			a: 65535,
		}
	});

	// Write
	{
		let mut writer = BufWriter::new (io::stdout ());

		write_farbfeld (&mut writer, &input_plane.size, sum_iter);
	}

	writeln!(&mut io::stderr(), "filter {:?}", filter).expect("failed printing to stderr");
}
<commit_msg>Making the blur way more efficient by making a bigger filter<commit_after>// Farbfeld filter that applies a cool blurring effect over images
// Usage: png2ff < greyscale.png | blur | ff2png > false-colored.png

use std::io;
use std::io::prelude::*;
use std::io::BufReader;
use std::io::BufWriter;

extern crate byteorder;
use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};

extern crate rayon;
use rayon::prelude::*;

#[derive (Clone, Copy)]
struct FfPixel {
	pub r: u16,
	pub g: u16,
	pub b: u16,
	pub a: u16,
}

#[derive (Clone, Copy, Debug, PartialEq)]
struct PlaneCoord {
	pub x: i32,
	pub y: i32,
}

impl PlaneCoord {
	pub fn contains_x (&self, x: i32) -> bool {
		x >= 0 && x < self.x
	}

	pub fn contains_y (&self, y: i32) -> bool {
		y >= 0 && y < self.y
	}

	pub fn contains (&self, x: i32, y: i32) -> bool {
		self.contains_x (x) &&
		self.contains_y (y)
	}

	pub fn clamp_x (&self, x: i32) -> i32 {
		return if x < 0 {
			0
		}
		else if x >= self.x {
			self.x - 1
		}
		else {
			x
		}
	}

	pub fn clamp_y (&self, y: i32) -> i32 {
		return if y < 0 {
			0
		}
		else if y >= self.y {
			self.y - 1
		}
		else {
			y
		}
	}

	pub fn clamp (&self, pc: &PlaneCoord) -> PlaneCoord {
		PlaneCoord {
			x: self.clamp_x (pc.x),
			y: self.clamp_y (pc.y),
		}
	}

	pub fn index (&self, pc: &PlaneCoord) -> i32 {
		let pc = self.clamp (pc);

		pc.y * self.x + pc.x
	}

	pub fn clamped_index (&self, pc: &PlaneCoord) -> i32 {
		self.index (&self.clamp (pc))
	}
}

struct HdrPlane {
	pub size: PlaneCoord,
	pub pixels: Vec <f64>,
}

impl HdrPlane {
	pub fn new (size: PlaneCoord) -> HdrPlane {
		HdrPlane {
			size: size,
			pixels: vec! [0.0; (size.x * size.y) as usize],
		}
	}
}

fn read_pixel <T> (reader: &mut T) -> FfPixel where T: ReadBytesExt {
	FfPixel {
		r: reader.read_u16::<BigEndian> ().unwrap (),
		g: reader.read_u16::<BigEndian> ().unwrap (),
		b: reader.read_u16::<BigEndian> ().unwrap (),
		a: reader.read_u16::<BigEndian> ().unwrap (),
	}
}

fn write_pixel <T> (writer: &mut T, pix: &FfPixel) where T: WriteBytesExt {
	writer.write_u16::<BigEndian> (pix.r).unwrap ();
	writer.write_u16::<BigEndian> (pix.g).unwrap ();
	writer.write_u16::<BigEndian> (pix.b).unwrap ();
	writer.write_u16::<BigEndian> (pix.a).unwrap ();
}

fn pascal (n: u32) -> Vec <u64> {
	match n {
		0 => {
			vec! []
		}
		1 => {
			vec! [1]
		},
		2 => {
			vec! [1, 1]
		},
		3 => {
			vec! [1, 2, 1]
		},
		4 => {
			vec! [1, 3, 3, 1]
		}
		5 => {
			vec! [1, 4, 6, 4, 1]
		},
		_ => {
			let step = 5;
			let prev_row = pascal (n - step + 1);
			let step_row = pascal (step);

			let mut output = vec! [];
			for _ in 0..n {
				output.push (0);
			}

			for a in 0..prev_row.len () {
				for b in 0..step_row.len () {
					output [a + b] += prev_row [a] * step_row [b];
				}
			}

			output
		}
	}
}

fn gaussian_filter (std_dev: i32) -> Vec <f64> {
	let scale = (2.0 * 3.14159265358979f64).sqrt () * std_dev as f64;
	let radius = 3 * std_dev;
	let pow_scale = (2 * std_dev * std_dev) as f64;

	(-radius..radius).map (|x| {
		let x = x as f64;
		((-x * x) / pow_scale).exp () / scale
	}).collect ()
}

struct ScanlineJob <'a> {
	pub y: i32,
	pub row_chunk: &'a mut [f64],
}

fn get_filter_offset (filter_len: i32) -> i32 {
	-(filter_len - 1) / 2
}

fn to_filter (filter_int: &[u64]) -> Vec <f64> {
	let sum: u64 = filter_int.iter ().sum ();
	filter_int.iter ().map (|i| *i as f64 / sum as f64).collect ()
}

fn blur_hor (input_plane: &HdrPlane, filter: &[f64]) -> HdrPlane {
	let offset = get_filter_offset (filter.len () as i32);

	let sz = input_plane.size;

	let mut pixels = vec! [0.0f64; (sz.x * sz.y) as usize];

	(0..sz.y).zip (pixels.chunks_mut (sz.x as usize)).map (|(y, row_chunk)| ScanlineJob { y: y, row_chunk: row_chunk }).collect::<Vec <ScanlineJob>> ().par_iter_mut ().for_each (|job| {
		let scanline_index = job.y * sz.x;

		for x in 0..sz.x {
			let offset_2 = x + offset;

			job.row_chunk [x as usize] = (sz.clamp_x (x + 0 + offset) - offset_2..sz.clamp_x (x + filter.len () as i32 + offset) - offset_2).map (|i| filter [i as usize] * input_plane.pixels [(scanline_index + i + offset_2) as usize]).sum ();
		}
	});

	HdrPlane {
		size: sz,
		pixels: pixels,
	}
}

fn blur_vert (input_plane: &HdrPlane, filter: &[f64]) -> HdrPlane {
	let offset = get_filter_offset (filter.len () as i32);

	let sz = input_plane.size;

	let mut pixels = vec! [0.0f64; (sz.x * sz.y) as usize];

	(0..sz.y).zip (pixels.chunks_mut (sz.x as usize)).map (|(y, row_chunk)| ScanlineJob { y: y, row_chunk: row_chunk }).collect::<Vec <ScanlineJob>> ().par_iter_mut ().for_each (|job| {
		for x in 0..sz.x {
			let offset_2 = job.y + offset;

			job.row_chunk [x as usize] = (sz.clamp_y (job.y + 0 + offset) - offset_2..sz.clamp_y (job.y + filter.len () as i32 + offset) - offset_2).map (|i| filter [i as usize] * input_plane.pixels [( sz.index (&PlaneCoord {x: x, y: i + offset_2})) as usize]).sum ();
		}
	});

	HdrPlane {
		size: sz,
		pixels: pixels,
	}
}

fn read_farbfeld_grey <T> (reader: &mut T) -> HdrPlane where T: ReadBytesExt {
	let mut magic = [0; 8];
	reader.read (&mut magic [..]).unwrap ();

	// TODO: Assert that magic == 'farbfeld'
	//assert_eq! (magic, &"farbfeld");

	let width = reader.read_u32::<BigEndian>().unwrap() as i32;
	let height = reader.read_u32::<BigEndian>().unwrap() as i32;

	writeln!(&mut io::stderr(), "{} x {}", width, height).expect("failed printing to stderr");

	let mut pixels = vec![0.0; (width * height) as usize];

	for i in 0 .. width * height {
		let gamma = read_pixel (reader).g as f64 / 65536.0;
		pixels [i as usize] = gamma * gamma;
	}

	HdrPlane {
		size: PlaneCoord {
			x: width,
			y: height,
		},
		pixels: pixels,
	}
}

fn write_farbfeld_plane <T> (writer: &mut T, plane: &HdrPlane) where T: WriteBytesExt
{
	write_farbfeld (writer, &plane.size, plane.pixels.iter ().map (|x| {
		let val = (x.sqrt ().min (1.0) * 65535.0) as u16;

		FfPixel {
			r: val,
			g: val,
			b: val,
			a: 65535,
		}
	}));
}

fn write_farbfeld <T, I> (writer: &mut T, size: &PlaneCoord, pixels: I ) where T: WriteBytesExt, I: Iterator <Item = FfPixel>
{
	writer.write ("farbfeld".as_bytes ()).expect ("printing farbfeld");

	writer.write_u32::<BigEndian> (size.x as u32).unwrap ();
	writer.write_u32::<BigEndian> (size.y as u32).unwrap ();

	for pixel in pixels.take ((size.x * size.y) as usize) {
		write_pixel (writer, &pixel);
	}
}

fn blur (input_plane: &HdrPlane, filter: &[f64]) -> HdrPlane {
	blur_vert (&blur_hor (input_plane, filter), filter)
	//blur_hor (input_plane, filter)
	//blur_vert (input_plane, filter)
}
/*
fn blur_n (input_plane: &HdrPlane, filter: &Vec <u64>, n: u32) -> HdrPlane {
	match n {
		0 => {
			// I know this is dumb
			panic! ("Can't blur an image 0 times")
		},
		1 => {
			blur (input_plane, filter)
		},
		_ => {
			blur (&blur_n (input_plane, filter, n - 1), filter)
		},
	}
}
*/
fn double_to_ff (val: f64) -> u16 {
	let max = 1.0;
	(val.min (max) * 65535.0 / max) as u16
}

fn to_linear (rgb: &[f64;3]) -> [f64;3] {
	[rgb [0] * rgb [0], rgb [1] * rgb [1], rgb [2] * rgb [2]]
}

fn to_gamma (rgb: &[f64;3]) -> [f64;3] {
	[rgb [0].sqrt (), rgb [1].sqrt (), rgb [2].sqrt ()]
}

fn alpha_blend (rgb_src: &[f64;3], rgb_dest: &[f64;3], alpha_src: f64) -> [f64;3]
{
	let mut rgb = [0.0, 0.0, 0.0];

	for i in 0..3 {
		let x = rgb_src [i] * alpha_src + rgb_dest [i] * (1.0 - alpha_src);
		rgb [i] = x;
	}

	rgb
}

fn main () {
	// Reader
	let mut reader = BufReader::new (io::stdin ());

	let input_plane = read_farbfeld_grey (&mut reader);

	let blurry_1 = blur (&input_plane, &gaussian_filter (1));
	let blurry_20 = blur (&input_plane, &gaussian_filter (5));
	let blurry_40 = blur (&input_plane, &gaussian_filter (40));

	let sum_iter = (0..(input_plane.size.x * input_plane.size.y) as usize).map (|i| {
		let rgb = to_linear (&[7.0 / 256.0, 1.0 / 256.0, 21.0 / 256.0]);

		let rgb = alpha_blend (&to_linear (&[210.0 / 256.0, 0.0 / 256.0, 156.0 / 256.0]), &rgb, blurry_40.pixels [i]);

		let rgb = alpha_blend (&to_linear (&[71.0 / 256.0, 203.0 / 256.0, 256.0 / 256.0]), &rgb, blurry_20.pixels [i]);

		let rgb = alpha_blend (&[1.0, 1.0, 1.0], &rgb, blurry_1.pixels [i]);

		let rgb = alpha_blend (&[1.0, 1.0, 1.0], &rgb, input_plane.pixels [i]);

		/*
		let r = input_plane.pixels [i];
		let g = r;
		let b = r;

		let r = r + blurry_1.pixels [i] * (71.0f64 / 256.0).powi (2);
		let g = g + blurry_1.pixels [i] * (203.0f64 / 256.0).powi (2);
		let b = b + blurry_1.pixels [i] * (256.0f64 / 256.0).powi (2);

		//let b = b + blurry_10.pixels [i] * 256.0 / 256.0;
		//let g = g + blurry_10.pixels [i] * 128.0 / 256.0;

		let r = r + blurry_20.pixels [i] * (210.0f64 / 256.0).powi (2);
		let g = g + blurry_20.pixels [i] * (0.0f64 / 256.0).powi (2);
		let b = b + blurry_20.pixels [i] * (156.0f64 / 256.0).powi (2);
		*/

		let rgb = to_gamma (&rgb);

		FfPixel {
			r: double_to_ff (rgb [0]),
			g: double_to_ff (rgb [1]),
			b: double_to_ff (rgb [2]),
			a: 65535,
		}
	});

	// Write
	{
		let mut writer = BufWriter::new (io::stdout ());

		write_farbfeld (&mut writer, &input_plane.size, sum_iter);
	}

	writeln!(&mut io::stderr(), "filter {:?}", gaussian_filter (5)).expect("failed printing to stderr");
}
<|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/. */

extern crate cc;
extern crate glsl_to_cxx;
extern crate webrender_build;

use std::collections::HashSet;
use std::fmt::Write;
use webrender_build::shader::{ShaderFeatureFlags, get_shader_features};

// Shader key is in "name feature,feature" format.
// File name needs to be formatted as "name_feature_feature".
fn shader_file(shader_key: &str) -> String {
    shader_key.replace(' ', "_").replace(',', "_")
}

fn write_load_shader(shader_keys: &[String]) {
    let mut load_shader = String::new();
    for s in shader_keys {
        let _ = write!(load_shader, "#include \"{}.h\"\n", shader_file(s));
    }
    load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
    for s in shader_keys {
        let _ = write!(load_shader, "  if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n",
                       s, shader_file(s));
    }
    load_shader.push_str("  return nullptr;\n}\n");
    std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap();
}

fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String>, output: &mut String) {
    if !included.insert(shader.into()) {
        return;
    }
    println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader);
    let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap();
    for line in source.lines() {
        if line.starts_with("#include ") {
            let imports = line["#include ".len() ..].split(',');
            for import in imports {
                process_imports(shader_dir, import, included, output);
            }
        } else if line.starts_with("#version ") || line.starts_with("#extension ") {
            // ignore
        } else {
            output.push_str(line);
            output.push('\n');
        }
    }
}

fn translate_shader(shader_key: &str, shader_dir: &str) {
    let mut imported = String::from("#define SWGL 1\n#define __VERSION__ 150\n");
    let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
                   webrender_build::MAX_VERTEX_TEXTURE_WIDTH);

    let (basename, features) =
        shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
    if !features.is_empty() {
        for feature in features.trim().split(',') {
            let _ = write!(imported, "#define WR_FEATURE_{}\n", feature);
        }
    }

    process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);

    let shader = shader_file(shader_key);

    let out_dir = std::env::var("OUT_DIR").unwrap();
    let imp_name = format!("{}/{}.c", out_dir, shader);
    std::fs::write(&imp_name, imported).unwrap();

    let mut build = cc::Build::new();
    build.no_default_flags(true);
    if let Ok(tool) = build.try_get_compiler() {
        if tool.is_like_msvc() {
            build.flag("/EP");
            if tool.path().to_str().map_or(false, |p| p.contains("clang")) {
                build.flag("/clang:-undef");
            } else {
                build.flag("/u");
            }
        } else {
            build.flag("-xc").flag("-P").flag("-undef");
        }
    }
    // Use SWGLPP target to avoid pulling CFLAGS/CXXFLAGS.
    build.target("SWGLPP");
    build.file(&imp_name);
    let vs = build.clone()
        .define("WR_VERTEX_SHADER", Some("1"))
        .expand();
    let fs = build.clone()
        .define("WR_FRAGMENT_SHADER", Some("1"))
        .expand();
    let vs_name = format!("{}/{}.vert", out_dir, shader);
    let fs_name = format!("{}/{}.frag", out_dir, shader);
    std::fs::write(&vs_name, vs).unwrap();
    std::fs::write(&fs_name, fs).unwrap();

    let args = vec![
        "glsl_to_cxx".to_string(),
        vs_name,
        fs_name,
    ];
    let result = glsl_to_cxx::translate(&mut args.into_iter());
    std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap();
}

fn main() {
    let shader_dir = match std::env::var("MOZ_SRC") {
        Ok(dir) => dir + "/gfx/wr/webrender/res",
        Err(_) => std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res",
    };

    let shader_flags =
        ShaderFeatureFlags::GL |
        ShaderFeatureFlags::DUAL_SOURCE_BLENDING |
        ShaderFeatureFlags::ADVANCED_BLEND_EQUATION |
        ShaderFeatureFlags::DEBUG;
    let mut shaders: Vec<String> = Vec::new();
    for (name, features) in get_shader_features(shader_flags) {
        shaders.extend(features.iter().map(|f| {
            if f.is_empty() { name.to_owned() } else { format!("{} {}", name, f) }
        }));
    }

    shaders.sort();

    // We need to ensure that the C preprocessor does not pull compiler flags from
    // the host or target environment. Set up a SWGLPP target with empty flags to
    // work around this.
    if let Ok(target) = std::env::var("TARGET") {
        if let Ok(cc) = std::env::var(format!("CC_{}", target))
                        .or(std::env::var(format!("CC_{}", target.replace("-", "_")))) {
            std::env::set_var("CC_SWGLPP", cc);
        }
    }
    std::env::set_var("CFLAGS_SWGLPP", "");

    for shader in &shaders {
        translate_shader(shader, &shader_dir);
    }

    write_load_shader(&shaders);

    println!("cargo:rerun-if-changed=src/blend.h");
    println!("cargo:rerun-if-changed=src/composite.h");
    println!("cargo:rerun-if-changed=src/gl_defs.h");
    println!("cargo:rerun-if-changed=src/glsl.h");
    println!("cargo:rerun-if-changed=src/program.h");
    println!("cargo:rerun-if-changed=src/rasterize.h");
    println!("cargo:rerun-if-changed=src/swgl_ext.h");
    println!("cargo:rerun-if-changed=src/texture.h");
    println!("cargo:rerun-if-changed=src/vector_type.h");
    println!("cargo:rerun-if-changed=src/gl.cc");
    let mut build = cc::Build::new();
    build.cpp(true);

    if let Ok(tool) = build.try_get_compiler() {
        if tool.is_like_msvc() {
            build.flag("/std:c++17")
                 .flag("/EHs-")
                 .flag("/GR-")
                 .flag("/UMOZILLA_CONFIG_H");
        } else {
            build.flag("-std=c++17")
                 .flag("-fno-exceptions")
                 .flag("-fno-rtti")
                 .flag("-fno-math-errno")
                 .flag("-UMOZILLA_CONFIG_H");
        }
        // SWGL relies heavily on inlining for performance so override -Oz with -O2
        if tool.args().contains(&"-Oz".into()) {
            build.flag("-O2");
        }
    }

    build.file("src/gl.cc")
        .define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
        .include(shader_dir)
        .include("src")
        .include(std::env::var("OUT_DIR").unwrap())
        .compile("gl_cc");
}
<commit_msg>Bug 1700717 - Build SWGL with -ffast-math. r=lsalzman<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/. */

extern crate cc;
extern crate glsl_to_cxx;
extern crate webrender_build;

use std::collections::HashSet;
use std::fmt::Write;
use webrender_build::shader::{ShaderFeatureFlags, get_shader_features};

// Shader key is in "name feature,feature" format.
// File name needs to be formatted as "name_feature_feature".
fn shader_file(shader_key: &str) -> String {
    shader_key.replace(' ', "_").replace(',', "_")
}

fn write_load_shader(shader_keys: &[String]) {
    let mut load_shader = String::new();
    for s in shader_keys {
        let _ = write!(load_shader, "#include \"{}.h\"\n", shader_file(s));
    }
    load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
    for s in shader_keys {
        let _ = write!(load_shader, "  if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n",
                       s, shader_file(s));
    }
    load_shader.push_str("  return nullptr;\n}\n");
    std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap();
}

fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String>, output: &mut String) {
    if !included.insert(shader.into()) {
        return;
    }
    println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader);
    let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap();
    for line in source.lines() {
        if line.starts_with("#include ") {
            let imports = line["#include ".len() ..].split(',');
            for import in imports {
                process_imports(shader_dir, import, included, output);
            }
        } else if line.starts_with("#version ") || line.starts_with("#extension ") {
            // ignore
        } else {
            output.push_str(line);
            output.push('\n');
        }
    }
}

fn translate_shader(shader_key: &str, shader_dir: &str) {
    let mut imported = String::from("#define SWGL 1\n#define __VERSION__ 150\n");
    let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
                   webrender_build::MAX_VERTEX_TEXTURE_WIDTH);

    let (basename, features) =
        shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
    if !features.is_empty() {
        for feature in features.trim().split(',') {
            let _ = write!(imported, "#define WR_FEATURE_{}\n", feature);
        }
    }

    process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);

    let shader = shader_file(shader_key);

    let out_dir = std::env::var("OUT_DIR").unwrap();
    let imp_name = format!("{}/{}.c", out_dir, shader);
    std::fs::write(&imp_name, imported).unwrap();

    let mut build = cc::Build::new();
    build.no_default_flags(true);
    if let Ok(tool) = build.try_get_compiler() {
        if tool.is_like_msvc() {
            build.flag("/EP");
            if tool.path().to_str().map_or(false, |p| p.contains("clang")) {
                build.flag("/clang:-undef");
            } else {
                build.flag("/u");
            }
        } else {
            build.flag("-xc").flag("-P").flag("-undef");
        }
    }
    // Use SWGLPP target to avoid pulling CFLAGS/CXXFLAGS.
    build.target("SWGLPP");
    build.file(&imp_name);
    let vs = build.clone()
        .define("WR_VERTEX_SHADER", Some("1"))
        .expand();
    let fs = build.clone()
        .define("WR_FRAGMENT_SHADER", Some("1"))
        .expand();
    let vs_name = format!("{}/{}.vert", out_dir, shader);
    let fs_name = format!("{}/{}.frag", out_dir, shader);
    std::fs::write(&vs_name, vs).unwrap();
    std::fs::write(&fs_name, fs).unwrap();

    let args = vec![
        "glsl_to_cxx".to_string(),
        vs_name,
        fs_name,
    ];
    let result = glsl_to_cxx::translate(&mut args.into_iter());
    std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap();
}

fn main() {
    let shader_dir = match std::env::var("MOZ_SRC") {
        Ok(dir) => dir + "/gfx/wr/webrender/res",
        Err(_) => std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res",
    };

    let shader_flags =
        ShaderFeatureFlags::GL |
        ShaderFeatureFlags::DUAL_SOURCE_BLENDING |
        ShaderFeatureFlags::ADVANCED_BLEND_EQUATION |
        ShaderFeatureFlags::DEBUG;
    let mut shaders: Vec<String> = Vec::new();
    for (name, features) in get_shader_features(shader_flags) {
        shaders.extend(features.iter().map(|f| {
            if f.is_empty() { name.to_owned() } else { format!("{} {}", name, f) }
        }));
    }

    shaders.sort();

    // We need to ensure that the C preprocessor does not pull compiler flags from
    // the host or target environment. Set up a SWGLPP target with empty flags to
    // work around this.
    if let Ok(target) = std::env::var("TARGET") {
        if let Ok(cc) = std::env::var(format!("CC_{}", target))
                        .or(std::env::var(format!("CC_{}", target.replace("-", "_")))) {
            std::env::set_var("CC_SWGLPP", cc);
        }
    }
    std::env::set_var("CFLAGS_SWGLPP", "");

    for shader in &shaders {
        translate_shader(shader, &shader_dir);
    }

    write_load_shader(&shaders);

    println!("cargo:rerun-if-changed=src/blend.h");
    println!("cargo:rerun-if-changed=src/composite.h");
    println!("cargo:rerun-if-changed=src/gl_defs.h");
    println!("cargo:rerun-if-changed=src/glsl.h");
    println!("cargo:rerun-if-changed=src/program.h");
    println!("cargo:rerun-if-changed=src/rasterize.h");
    println!("cargo:rerun-if-changed=src/swgl_ext.h");
    println!("cargo:rerun-if-changed=src/texture.h");
    println!("cargo:rerun-if-changed=src/vector_type.h");
    println!("cargo:rerun-if-changed=src/gl.cc");
    let mut build = cc::Build::new();
    build.cpp(true);

    if let Ok(tool) = build.try_get_compiler() {
        if tool.is_like_msvc() {
            build.flag("/std:c++17")
                 .flag("/EHs-")
                 .flag("/GR-")
                 .flag("/UMOZILLA_CONFIG_H");
        } else {
            build.flag("-std=c++17")
                 .flag("-fno-exceptions")
                 .flag("-fno-rtti")
                 .flag("-fno-math-errno")
                 .flag("-UMOZILLA_CONFIG_H");
        }
        // SWGL relies heavily on inlining for performance so override -Oz with -O2
        if tool.args().contains(&"-Oz".into()) {
            build.flag("-O2");
        }

        // Most GLSL compilers assume something like fast-math so we turn it on.
        // However, reciprocal division makes it so 1/1 = 0.999994 which can produce a lot of fuzz
        // in reftests and the use of reciprocal instructions usually involves a refinement step
        // which bloats our already bloated code. Further, our shader code is sufficiently parallel
        // that we're more likely to be throughput bound vs latency bound. Having fewer
        // instructions makes things easier on the processor and in places where it matters we can
        // probably explicitly use reciprocal instructions and avoid the refinement step.
        if tool.is_like_msvc() {
            build.flag("/fp:fast")
                 .flag("-Xclang")
                 .flag("-mrecip=none");
        } else {
            build.flag("-ffast-math")
                 .flag("-mrecip=none");
        }
    }

    build.file("src/gl.cc")
        .define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
        .include(shader_dir)
        .include("src")
        .include(std::env::var("OUT_DIR").unwrap())
        .compile("gl_cc");
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate serde_elm;

#[allow(dead_code)]
#[derive(Elm)]
struct Simple {
    name: String
}

#[test]
fn should_assert_elm_generation_was_done() {
    assert!(true);
}
<commit_msg>Test is the file actually is created<commit_after>#[macro_use]
extern crate serde_elm;

use std::env;

#[allow(dead_code)]
#[derive(Elm)]
struct Simple {
    name: String
}

#[test]
fn should_assert_elm_generation_was_done() {
    let mut path = env::current_dir().unwrap();
    path.push("generated");
    path.push("Simple.elm");

    assert!(path.exists());
    assert!(path.is_file());
}
<|endoftext|>


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

<commit_msg>Remove macros.rs<commit_after><|endoftext|>


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

use common::*;
use syntax::ast;
use syntax::ast_util::local_def;
use syntax::ast_map::{path, path_mod, path_name};
use base::{trans_item, get_item_val, self_arg, trans_fn, impl_owned_self,
           impl_self, no_self, get_insn_ctxt};

// `translate` will be true if this function is allowed to translate the
// item and false otherwise. Currently, this parameter is set to false when
// translating default methods.
fn maybe_instantiate_inline(ccx: @crate_ctxt, fn_id: ast::def_id,
                            translate: bool)
    -> ast::def_id {
    let _icx = ccx.insn_ctxt("maybe_instantiate_inline");
    match ccx.external.find(fn_id) {
      Some(Some(node_id)) => {
        // Already inline
        debug!("maybe_instantiate_inline(%s): already inline as node id %d",
               ty::item_path_str(ccx.tcx, fn_id), node_id);
        local_def(node_id)
      }
      Some(None) => fn_id, // Not inlinable
      None => { // Not seen yet
        match csearch::maybe_get_item_ast(
            ccx.tcx, fn_id,
            |a,b,c,d| {
                astencode::decode_inlined_item(a, b, ccx.maps, c, d)
            }) {

          csearch::not_found => {
            ccx.external.insert(fn_id, None);
            fn_id
          }
          csearch::found(ast::ii_item(item)) => {
            ccx.external.insert(fn_id, Some(item.id));
            ccx.stats.n_inlines += 1;
            if translate { trans_item(ccx, *item); }
            local_def(item.id)
          }
          csearch::found(ast::ii_foreign(item)) => {
            ccx.external.insert(fn_id, Some(item.id));
            local_def(item.id)
          }
          csearch::found_parent(parent_id, ast::ii_item(item)) => {
            ccx.external.insert(parent_id, Some(item.id));
            let mut my_id = 0;
            match item.node {
              ast::item_enum(_, _) => {
                let vs_here = ty::enum_variants(ccx.tcx, local_def(item.id));
                let vs_there = ty::enum_variants(ccx.tcx, parent_id);
                for vec::each2(*vs_here, *vs_there) |here, there| {
                    if there.id == fn_id { my_id = here.id.node; }
                    ccx.external.insert(there.id, Some(here.id.node));
                }
              }
              _ => ccx.sess.bug(~"maybe_instantiate_inline: item has a \
                    non-enum parent")
            }
            if translate { trans_item(ccx, *item); }
            local_def(my_id)
          }
          csearch::found_parent(_, _) => {
              ccx.sess.bug(~"maybe_get_item_ast returned a found_parent \
               with a non-item parent");
          }
          csearch::found(ast::ii_method(impl_did, mth)) => {
            ccx.stats.n_inlines += 1;
            ccx.external.insert(fn_id, Some(mth.id));
            let {bounds: impl_bnds, region_param: _, ty: _} =
                ty::lookup_item_type(ccx.tcx, impl_did);
            if translate && (*impl_bnds).len() + mth.tps.len() == 0u {
                let llfn = get_item_val(ccx, mth.id);
                let path = vec::append(
                    ty::item_path(ccx.tcx, impl_did),
                    ~[path_name(mth.ident)]);
                let self_kind = match mth.self_ty.node {
                    ast::sty_static => no_self,
                    _ => {
                        let self_ty = ty::node_id_to_type(ccx.tcx, mth.self_id);
                        debug!("calling inline trans_fn with self_ty %s",
                               ty_to_str(ccx.tcx, self_ty));
                        match mth.self_ty.node {
                            ast::sty_value => impl_owned_self(self_ty),
                            _ => impl_self(self_ty),
                        }
                    }
                };
                trans_fn(ccx,
                         path,
                         mth.decl,
                         mth.body,
                         llfn,
                         self_kind,
                         None,
                         mth.id,
                         Some(impl_did));
            }
            local_def(mth.id)
          }
          csearch::found(ast::ii_dtor(ref dtor, _, _, _)) => {
              ccx.external.insert(fn_id, Some((*dtor).node.id));
              local_def((*dtor).node.id)
          }
        }
      }
    }
}

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

use common::*;
use syntax::ast;
use syntax::ast_util::local_def;
use syntax::ast_map::{path, path_mod, path_name};
use base::{trans_item, get_item_val, self_arg, trans_fn, impl_owned_self,
           impl_self, no_self, get_insn_ctxt};

// `translate` will be true if this function is allowed to translate the
// item and false otherwise. Currently, this parameter is set to false when
// translating default methods.
fn maybe_instantiate_inline(ccx: @crate_ctxt, fn_id: ast::def_id,
                            translate: bool)
    -> ast::def_id {
    let _icx = ccx.insn_ctxt("maybe_instantiate_inline");
    match ccx.external.find(fn_id) {
      Some(Some(node_id)) => {
        // Already inline
        debug!("maybe_instantiate_inline(%s): already inline as node id %d",
               ty::item_path_str(ccx.tcx, fn_id), node_id);
        local_def(node_id)
      }
      Some(None) => fn_id, // Not inlinable
      None => { // Not seen yet
        match csearch::maybe_get_item_ast(
            ccx.tcx, fn_id,
            |a,b,c,d| {
                astencode::decode_inlined_item(a, b, ccx.maps, c, d)
            }) {

          csearch::not_found => {
            ccx.external.insert(fn_id, None);
            fn_id
          }
          csearch::found(ast::ii_item(item)) => {
            ccx.external.insert(fn_id, Some(item.id));
            ccx.stats.n_inlines += 1;
            if translate { trans_item(ccx, *item); }
            local_def(item.id)
          }
          csearch::found(ast::ii_foreign(item)) => {
            ccx.external.insert(fn_id, Some(item.id));
            local_def(item.id)
          }
          csearch::found_parent(parent_id, ast::ii_item(item)) => {
            ccx.external.insert(parent_id, Some(item.id));
            let mut my_id = 0;
            match item.node {
              ast::item_enum(_, _) => {
                let vs_here = ty::enum_variants(ccx.tcx, local_def(item.id));
                let vs_there = ty::enum_variants(ccx.tcx, parent_id);
                for vec::each2(*vs_here, *vs_there) |here, there| {
                    if there.id == fn_id { my_id = here.id.node; }
                    ccx.external.insert(there.id, Some(here.id.node));
                }
              }
              _ => ccx.sess.bug(~"maybe_instantiate_inline: item has a \
                    non-enum parent")
            }
            if translate { trans_item(ccx, *item); }
            local_def(my_id)
          }
          csearch::found_parent(_, _) => {
              ccx.sess.bug(~"maybe_get_item_ast returned a found_parent \
               with a non-item parent");
          }
          csearch::found(ast::ii_method(impl_did, mth)) => {
            ccx.stats.n_inlines += 1;
            ccx.external.insert(fn_id, Some(mth.id));
            let {bounds: impl_bnds, region_param: _, ty: _} =
                ty::lookup_item_type(ccx.tcx, impl_did);
            if translate && (*impl_bnds).len() + mth.tps.len() == 0u {
                let llfn = get_item_val(ccx, mth.id);
                let path = vec::append(
                    ty::item_path(ccx.tcx, impl_did),
                    ~[path_name(mth.ident)]);
                let self_kind = match mth.self_ty.node {
                    ast::sty_static => no_self,
                    _ => {
                        let self_ty = ty::node_id_to_type(ccx.tcx,
                                                          mth.self_id);
                        debug!("calling inline trans_fn with self_ty %s",
                               ty_to_str(ccx.tcx, self_ty));
                        match mth.self_ty.node {
                            ast::sty_value => impl_owned_self(self_ty),
                            _ => impl_self(self_ty),
                        }
                    }
                };
                trans_fn(ccx,
                         path,
                         mth.decl,
                         mth.body,
                         llfn,
                         self_kind,
                         None,
                         mth.id,
                         Some(impl_did));
            }
            local_def(mth.id)
          }
          csearch::found(ast::ii_dtor(ref dtor, _, _, _)) => {
              ccx.external.insert(fn_id, Some((*dtor).node.id));
              local_def((*dtor).node.id)
          }
        }
      }
    }
}

<|endoftext|>


<commit_before>use super::item::ItemInfo;
use super::Parser;

use crate::{new_sub_parser_from_file, DirectoryOwnership};

use rustc_ast::ast::{self, Attribute, Crate, Ident, ItemKind, Mod};
use rustc_ast::attr;
use rustc_ast::token::{self, TokenKind};
use rustc_errors::{struct_span_err, PResult};
use rustc_session::parse::ParseSess;
use rustc_span::source_map::{FileName, Span, DUMMY_SP};
use rustc_span::symbol::sym;

use std::path::{self, Path, PathBuf};

/// Information about the path to a module.
// Public for rustfmt usage.
pub struct ModulePath<'a> {
    name: String,
    path_exists: bool,
    pub result: PResult<'a, ModulePathSuccess>,
}

// Public for rustfmt usage.
pub struct ModulePathSuccess {
    pub path: PathBuf,
    pub directory_ownership: DirectoryOwnership,
}

impl<'a> Parser<'a> {
    /// Parses a source module as a crate. This is the main entry point for the parser.
    pub fn parse_crate_mod(&mut self) -> PResult<'a, Crate> {
        let lo = self.token.span;
        let (module, attrs) = self.parse_mod(&token::Eof)?;
        let span = lo.to(self.token.span);
        let proc_macros = Vec::new(); // Filled in by `proc_macro_harness::inject()`.
        Ok(ast::Crate { attrs, module, span, proc_macros })
    }

    /// Parses a `mod <foo> { ... }` or `mod <foo>;` item.
    pub(super) fn parse_item_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> {
        let in_cfg = crate::config::process_configure_mod(self.sess, self.cfg_mods, attrs);

        let id = self.parse_ident()?;
        let (module, mut inner_attrs) = if self.eat(&token::Semi) {
            if in_cfg && self.recurse_into_file_modules {
                // This mod is in an external file. Let's go get it!
                let ModulePathSuccess { path, directory_ownership } = submod_path(
                    self.sess,
                    id,
                    &attrs,
                    self.directory.ownership,
                    &self.directory.path,
                )?;
                self.eval_src_mod(path, directory_ownership, id.to_string(), id.span)?
            } else {
                (ast::Mod { inner: DUMMY_SP, items: Vec::new(), inline: false }, Vec::new())
            }
        } else {
            let old_directory = self.directory.clone();
            push_directory(id, &attrs, &mut self.directory.ownership, &mut self.directory.path);

            self.expect(&token::OpenDelim(token::Brace))?;
            let module = self.parse_mod(&token::CloseDelim(token::Brace))?;

            self.directory = old_directory;
            module
        };
        attrs.append(&mut inner_attrs);
        Ok((id, ItemKind::Mod(module)))
    }

    /// Parses the contents of a module (inner attributes followed by module items).
    fn parse_mod(&mut self, term: &TokenKind) -> PResult<'a, (Mod, Vec<Attribute>)> {
        let lo = self.token.span;
        let attrs = self.parse_inner_attributes()?;
        let module = self.parse_mod_items(term, lo)?;
        Ok((module, attrs))
    }

    /// Given a termination token, parses all of the items in a module.
    fn parse_mod_items(&mut self, term: &TokenKind, inner_lo: Span) -> PResult<'a, Mod> {
        let mut items = vec![];
        while let Some(item) = self.parse_item()? {
            items.push(item);
            self.maybe_consume_incorrect_semicolon(&items);
        }

        if !self.eat(term) {
            let token_str = super::token_descr(&self.token);
            if !self.maybe_consume_incorrect_semicolon(&items) {
                let msg = &format!("expected item, found {}", token_str);
                let mut err = self.struct_span_err(self.token.span, msg);
                err.span_label(self.token.span, "expected item");
                return Err(err);
            }
        }

        let hi = if self.token.span.is_dummy() { inner_lo } else { self.prev_token.span };

        Ok(Mod { inner: inner_lo.to(hi), items, inline: true })
    }

    /// Reads a module from a source file.
    fn eval_src_mod(
        &mut self,
        path: PathBuf,
        directory_ownership: DirectoryOwnership,
        name: String,
        id_sp: Span,
    ) -> PResult<'a, (Mod, Vec<Attribute>)> {
        let mut included_mod_stack = self.sess.included_mod_stack.borrow_mut();
        self.error_on_circular_module(id_sp, &path, &included_mod_stack)?;
        included_mod_stack.push(path.clone());
        drop(included_mod_stack);

        let mut p0 =
            new_sub_parser_from_file(self.sess, &path, directory_ownership, Some(name), id_sp);
        p0.cfg_mods = self.cfg_mods;
        let mut module = p0.parse_mod(&token::Eof)?;
        module.0.inline = false;

        self.sess.included_mod_stack.borrow_mut().pop();
        Ok(module)
    }

    fn error_on_circular_module(
        &self,
        span: Span,
        path: &Path,
        included_mod_stack: &[PathBuf],
    ) -> PResult<'a, ()> {
        if let Some(i) = included_mod_stack.iter().position(|p| *p == path) {
            let mut err = String::from("circular modules: ");
            let len = included_mod_stack.len();
            for p in &included_mod_stack[i..len] {
                err.push_str(&p.to_string_lossy());
                err.push_str(" -> ");
            }
            err.push_str(&path.to_string_lossy());
            return Err(self.struct_span_err(span, &err[..]));
        }
        Ok(())
    }
}

fn push_directory(
    id: Ident,
    attrs: &[Attribute],
    dir_ownership: &mut DirectoryOwnership,
    dir_path: &mut PathBuf,
) {
    if let Some(path) = attr::first_attr_value_str_by_name(attrs, sym::path) {
        dir_path.push(&*path.as_str());
        *dir_ownership = DirectoryOwnership::Owned { relative: None };
    } else {
        // We have to push on the current module name in the case of relative
        // paths in order to ensure that any additional module paths from inline
        // `mod x { ... }` come after the relative extension.
        //
        // For example, a `mod z { ... }` inside `x/y.rs` should set the current
        // directory path to `/x/y/z`, not `/x/z` with a relative offset of `y`.
        if let DirectoryOwnership::Owned { relative } = dir_ownership {
            if let Some(ident) = relative.take() {
                // Remove the relative offset.
                dir_path.push(&*ident.as_str());
            }
        }
        dir_path.push(&*id.as_str());
    }
}

fn submod_path<'a>(
    sess: &'a ParseSess,
    id: ast::Ident,
    outer_attrs: &[Attribute],
    directory_ownership: DirectoryOwnership,
    dir_path: &Path,
) -> PResult<'a, ModulePathSuccess> {
    if let Some(path) = submod_path_from_attr(outer_attrs, dir_path) {
        let directory_ownership = match path.file_name().and_then(|s| s.to_str()) {
            // All `#[path]` files are treated as though they are a `mod.rs` file.
            // This means that `mod foo;` declarations inside `#[path]`-included
            // files are siblings,
            //
            // Note that this will produce weirdness when a file named `foo.rs` is
            // `#[path]` included and contains a `mod foo;` declaration.
            // If you encounter this, it's your own darn fault :P
            Some(_) => DirectoryOwnership::Owned { relative: None },
            _ => DirectoryOwnership::UnownedViaMod,
        };
        return Ok(ModulePathSuccess { directory_ownership, path });
    }

    let relative = match directory_ownership {
        DirectoryOwnership::Owned { relative } => relative,
        DirectoryOwnership::UnownedViaBlock | DirectoryOwnership::UnownedViaMod => None,
    };
    let ModulePath { path_exists, name, result } =
        default_submod_path(sess, id, relative, dir_path);
    match directory_ownership {
        DirectoryOwnership::Owned { .. } => Ok(result?),
        DirectoryOwnership::UnownedViaBlock => {
            let _ = result.map_err(|mut err| err.cancel());
            error_decl_mod_in_block(sess, id.span, path_exists, &name)
        }
        DirectoryOwnership::UnownedViaMod => {
            let _ = result.map_err(|mut err| err.cancel());
            error_cannot_declare_mod_here(sess, id.span, path_exists, &name)
        }
    }
}

fn error_decl_mod_in_block<'a, T>(
    sess: &'a ParseSess,
    id_sp: Span,
    path_exists: bool,
    name: &str,
) -> PResult<'a, T> {
    let msg = "Cannot declare a non-inline module inside a block unless it has a path attribute";
    let mut err = sess.span_diagnostic.struct_span_err(id_sp, msg);
    if path_exists {
        let msg = format!("Maybe `use` the module `{}` instead of redeclaring it", name);
        err.span_note(id_sp, &msg);
    }
    Err(err)
}

fn error_cannot_declare_mod_here<'a, T>(
    sess: &'a ParseSess,
    id_sp: Span,
    path_exists: bool,
    name: &str,
) -> PResult<'a, T> {
    let mut err =
        sess.span_diagnostic.struct_span_err(id_sp, "cannot declare a new module at this location");
    if !id_sp.is_dummy() {
        if let FileName::Real(src_path) = sess.source_map().span_to_filename(id_sp) {
            if let Some(stem) = src_path.file_stem() {
                let mut dest_path = src_path.clone();
                dest_path.set_file_name(stem);
                dest_path.push("mod.rs");
                err.span_note(
                    id_sp,
                    &format!(
                        "maybe move this module `{}` to its own \
                                directory via `{}`",
                        src_path.display(),
                        dest_path.display()
                    ),
                );
            }
        }
    }
    if path_exists {
        err.span_note(
            id_sp,
            &format!("... or maybe `use` the module `{}` instead of possibly redeclaring it", name),
        );
    }
    Err(err)
}

/// Derive a submodule path from the first found `#[path = "path_string"]`.
/// The provided `dir_path` is joined with the `path_string`.
// Public for rustfmt usage.
pub fn submod_path_from_attr(attrs: &[Attribute], dir_path: &Path) -> Option<PathBuf> {
    // Extract path string from first `#[path = "path_string"]` attribute.
    let path_string = attr::first_attr_value_str_by_name(attrs, sym::path)?;
    let path_string = path_string.as_str();

    // On windows, the base path might have the form
    // `\\?\foo\bar` in which case it does not tolerate
    // mixed `/` and `\` separators, so canonicalize
    // `/` to `\`.
    #[cfg(windows)]
    let path_string = path_string.replace("/", "\\");

    Some(dir_path.join(&*path_string))
}

/// Returns a path to a module.
// Public for rustfmt usage.
pub fn default_submod_path<'a>(
    sess: &'a ParseSess,
    id: ast::Ident,
    relative: Option<ast::Ident>,
    dir_path: &Path,
) -> ModulePath<'a> {
    // If we're in a foo.rs file instead of a mod.rs file,
    // we need to look for submodules in
    // `./foo/<id>.rs` and `./foo/<id>/mod.rs` rather than
    // `./<id>.rs` and `./<id>/mod.rs`.
    let relative_prefix_string;
    let relative_prefix = if let Some(ident) = relative {
        relative_prefix_string = format!("{}{}", ident.name, path::MAIN_SEPARATOR);
        &relative_prefix_string
    } else {
        ""
    };

    let mod_name = id.name.to_string();
    let default_path_str = format!("{}{}.rs", relative_prefix, mod_name);
    let secondary_path_str =
        format!("{}{}{}mod.rs", relative_prefix, mod_name, path::MAIN_SEPARATOR);
    let default_path = dir_path.join(&default_path_str);
    let secondary_path = dir_path.join(&secondary_path_str);
    let default_exists = sess.source_map().file_exists(&default_path);
    let secondary_exists = sess.source_map().file_exists(&secondary_path);

    let result = match (default_exists, secondary_exists) {
        (true, false) => Ok(ModulePathSuccess {
            path: default_path,
            directory_ownership: DirectoryOwnership::Owned { relative: Some(id) },
        }),
        (false, true) => Ok(ModulePathSuccess {
            path: secondary_path,
            directory_ownership: DirectoryOwnership::Owned { relative: None },
        }),
        (false, false) => {
            let mut err = struct_span_err!(
                sess.span_diagnostic,
                id.span,
                E0583,
                "file not found for module `{}`",
                mod_name,
            );
            err.help(&format!(
                "to create the module `{}`, create file \"{}\"",
                mod_name,
                default_path.display(),
            ));
            Err(err)
        }
        (true, true) => {
            let mut err = struct_span_err!(
                sess.span_diagnostic,
                id.span,
                E0584,
                "file for module `{}` found at both {} and {}",
                mod_name,
                default_path_str,
                secondary_path_str,
            );
            err.help("delete or rename one of them to remove the ambiguity");
            Err(err)
        }
    };

    ModulePath { name: mod_name, path_exists: default_exists || secondary_exists, result }
}
<commit_msg>decouple eval_src_mod from Parser<commit_after>use super::item::ItemInfo;
use super::Parser;

use crate::{new_sub_parser_from_file, DirectoryOwnership};

use rustc_ast::ast::{self, Attribute, Crate, Ident, ItemKind, Mod};
use rustc_ast::attr;
use rustc_ast::token::{self, TokenKind};
use rustc_errors::{struct_span_err, PResult};
use rustc_session::parse::ParseSess;
use rustc_span::source_map::{FileName, Span, DUMMY_SP};
use rustc_span::symbol::sym;

use std::path::{self, Path, PathBuf};

/// Information about the path to a module.
// Public for rustfmt usage.
pub struct ModulePath<'a> {
    name: String,
    path_exists: bool,
    pub result: PResult<'a, ModulePathSuccess>,
}

// Public for rustfmt usage.
pub struct ModulePathSuccess {
    pub path: PathBuf,
    pub directory_ownership: DirectoryOwnership,
}

impl<'a> Parser<'a> {
    /// Parses a source module as a crate. This is the main entry point for the parser.
    pub fn parse_crate_mod(&mut self) -> PResult<'a, Crate> {
        let lo = self.token.span;
        let (module, attrs) = self.parse_mod(&token::Eof)?;
        let span = lo.to(self.token.span);
        let proc_macros = Vec::new(); // Filled in by `proc_macro_harness::inject()`.
        Ok(ast::Crate { attrs, module, span, proc_macros })
    }

    /// Parses a `mod <foo> { ... }` or `mod <foo>;` item.
    pub(super) fn parse_item_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> {
        let in_cfg = crate::config::process_configure_mod(self.sess, self.cfg_mods, attrs);

        let id = self.parse_ident()?;
        let (module, mut inner_attrs) = if self.eat(&token::Semi) {
            if in_cfg && self.recurse_into_file_modules {
                // This mod is in an external file. Let's go get it!
                let ModulePathSuccess { path, directory_ownership } = submod_path(
                    self.sess,
                    id,
                    &attrs,
                    self.directory.ownership,
                    &self.directory.path,
                )?;
                eval_src_mod(self.sess, self.cfg_mods, path, directory_ownership, id)?
            } else {
                (ast::Mod { inner: DUMMY_SP, items: Vec::new(), inline: false }, Vec::new())
            }
        } else {
            let old_directory = self.directory.clone();
            push_directory(id, &attrs, &mut self.directory.ownership, &mut self.directory.path);

            self.expect(&token::OpenDelim(token::Brace))?;
            let module = self.parse_mod(&token::CloseDelim(token::Brace))?;

            self.directory = old_directory;
            module
        };
        attrs.append(&mut inner_attrs);
        Ok((id, ItemKind::Mod(module)))
    }

    /// Parses the contents of a module (inner attributes followed by module items).
    fn parse_mod(&mut self, term: &TokenKind) -> PResult<'a, (Mod, Vec<Attribute>)> {
        let lo = self.token.span;
        let attrs = self.parse_inner_attributes()?;
        let module = self.parse_mod_items(term, lo)?;
        Ok((module, attrs))
    }

    /// Given a termination token, parses all of the items in a module.
    fn parse_mod_items(&mut self, term: &TokenKind, inner_lo: Span) -> PResult<'a, Mod> {
        let mut items = vec![];
        while let Some(item) = self.parse_item()? {
            items.push(item);
            self.maybe_consume_incorrect_semicolon(&items);
        }

        if !self.eat(term) {
            let token_str = super::token_descr(&self.token);
            if !self.maybe_consume_incorrect_semicolon(&items) {
                let msg = &format!("expected item, found {}", token_str);
                let mut err = self.struct_span_err(self.token.span, msg);
                err.span_label(self.token.span, "expected item");
                return Err(err);
            }
        }

        let hi = if self.token.span.is_dummy() { inner_lo } else { self.prev_token.span };

        Ok(Mod { inner: inner_lo.to(hi), items, inline: true })
    }
}

/// Reads a module from a source file.
fn eval_src_mod<'a>(
    sess: &'a ParseSess,
    cfg_mods: bool,
    path: PathBuf,
    dir_ownership: DirectoryOwnership,
    id: ast::Ident,
) -> PResult<'a, (Mod, Vec<Attribute>)> {
    let mut included_mod_stack = sess.included_mod_stack.borrow_mut();
    if let Some(i) = included_mod_stack.iter().position(|p| *p == path) {
        let mut err = String::from("circular modules: ");
        for p in &included_mod_stack[i..] {
            err.push_str(&p.to_string_lossy());
            err.push_str(" -> ");
        }
        err.push_str(&path.to_string_lossy());
        return Err(sess.span_diagnostic.struct_span_err(id.span, &err[..]));
    }
    included_mod_stack.push(path.clone());
    drop(included_mod_stack);

    let mut p0 =
        new_sub_parser_from_file(sess, &path, dir_ownership, Some(id.to_string()), id.span);
    p0.cfg_mods = cfg_mods;
    let mut module = p0.parse_mod(&token::Eof)?;
    module.0.inline = false;

    sess.included_mod_stack.borrow_mut().pop();
    Ok(module)
}

fn push_directory(
    id: Ident,
    attrs: &[Attribute],
    dir_ownership: &mut DirectoryOwnership,
    dir_path: &mut PathBuf,
) {
    if let Some(path) = attr::first_attr_value_str_by_name(attrs, sym::path) {
        dir_path.push(&*path.as_str());
        *dir_ownership = DirectoryOwnership::Owned { relative: None };
    } else {
        // We have to push on the current module name in the case of relative
        // paths in order to ensure that any additional module paths from inline
        // `mod x { ... }` come after the relative extension.
        //
        // For example, a `mod z { ... }` inside `x/y.rs` should set the current
        // directory path to `/x/y/z`, not `/x/z` with a relative offset of `y`.
        if let DirectoryOwnership::Owned { relative } = dir_ownership {
            if let Some(ident) = relative.take() {
                // Remove the relative offset.
                dir_path.push(&*ident.as_str());
            }
        }
        dir_path.push(&*id.as_str());
    }
}

fn submod_path<'a>(
    sess: &'a ParseSess,
    id: ast::Ident,
    outer_attrs: &[Attribute],
    directory_ownership: DirectoryOwnership,
    dir_path: &Path,
) -> PResult<'a, ModulePathSuccess> {
    if let Some(path) = submod_path_from_attr(outer_attrs, dir_path) {
        let directory_ownership = match path.file_name().and_then(|s| s.to_str()) {
            // All `#[path]` files are treated as though they are a `mod.rs` file.
            // This means that `mod foo;` declarations inside `#[path]`-included
            // files are siblings,
            //
            // Note that this will produce weirdness when a file named `foo.rs` is
            // `#[path]` included and contains a `mod foo;` declaration.
            // If you encounter this, it's your own darn fault :P
            Some(_) => DirectoryOwnership::Owned { relative: None },
            _ => DirectoryOwnership::UnownedViaMod,
        };
        return Ok(ModulePathSuccess { directory_ownership, path });
    }

    let relative = match directory_ownership {
        DirectoryOwnership::Owned { relative } => relative,
        DirectoryOwnership::UnownedViaBlock | DirectoryOwnership::UnownedViaMod => None,
    };
    let ModulePath { path_exists, name, result } =
        default_submod_path(sess, id, relative, dir_path);
    match directory_ownership {
        DirectoryOwnership::Owned { .. } => Ok(result?),
        DirectoryOwnership::UnownedViaBlock => {
            let _ = result.map_err(|mut err| err.cancel());
            error_decl_mod_in_block(sess, id.span, path_exists, &name)
        }
        DirectoryOwnership::UnownedViaMod => {
            let _ = result.map_err(|mut err| err.cancel());
            error_cannot_declare_mod_here(sess, id.span, path_exists, &name)
        }
    }
}

fn error_decl_mod_in_block<'a, T>(
    sess: &'a ParseSess,
    id_sp: Span,
    path_exists: bool,
    name: &str,
) -> PResult<'a, T> {
    let msg = "Cannot declare a non-inline module inside a block unless it has a path attribute";
    let mut err = sess.span_diagnostic.struct_span_err(id_sp, msg);
    if path_exists {
        let msg = format!("Maybe `use` the module `{}` instead of redeclaring it", name);
        err.span_note(id_sp, &msg);
    }
    Err(err)
}

fn error_cannot_declare_mod_here<'a, T>(
    sess: &'a ParseSess,
    id_sp: Span,
    path_exists: bool,
    name: &str,
) -> PResult<'a, T> {
    let mut err =
        sess.span_diagnostic.struct_span_err(id_sp, "cannot declare a new module at this location");
    if !id_sp.is_dummy() {
        if let FileName::Real(src_path) = sess.source_map().span_to_filename(id_sp) {
            if let Some(stem) = src_path.file_stem() {
                let mut dest_path = src_path.clone();
                dest_path.set_file_name(stem);
                dest_path.push("mod.rs");
                err.span_note(
                    id_sp,
                    &format!(
                        "maybe move this module `{}` to its own \
                                directory via `{}`",
                        src_path.display(),
                        dest_path.display()
                    ),
                );
            }
        }
    }
    if path_exists {
        err.span_note(
            id_sp,
            &format!("... or maybe `use` the module `{}` instead of possibly redeclaring it", name),
        );
    }
    Err(err)
}

/// Derive a submodule path from the first found `#[path = "path_string"]`.
/// The provided `dir_path` is joined with the `path_string`.
// Public for rustfmt usage.
pub fn submod_path_from_attr(attrs: &[Attribute], dir_path: &Path) -> Option<PathBuf> {
    // Extract path string from first `#[path = "path_string"]` attribute.
    let path_string = attr::first_attr_value_str_by_name(attrs, sym::path)?;
    let path_string = path_string.as_str();

    // On windows, the base path might have the form
    // `\\?\foo\bar` in which case it does not tolerate
    // mixed `/` and `\` separators, so canonicalize
    // `/` to `\`.
    #[cfg(windows)]
    let path_string = path_string.replace("/", "\\");

    Some(dir_path.join(&*path_string))
}

/// Returns a path to a module.
// Public for rustfmt usage.
pub fn default_submod_path<'a>(
    sess: &'a ParseSess,
    id: ast::Ident,
    relative: Option<ast::Ident>,
    dir_path: &Path,
) -> ModulePath<'a> {
    // If we're in a foo.rs file instead of a mod.rs file,
    // we need to look for submodules in
    // `./foo/<id>.rs` and `./foo/<id>/mod.rs` rather than
    // `./<id>.rs` and `./<id>/mod.rs`.
    let relative_prefix_string;
    let relative_prefix = if let Some(ident) = relative {
        relative_prefix_string = format!("{}{}", ident.name, path::MAIN_SEPARATOR);
        &relative_prefix_string
    } else {
        ""
    };

    let mod_name = id.name.to_string();
    let default_path_str = format!("{}{}.rs", relative_prefix, mod_name);
    let secondary_path_str =
        format!("{}{}{}mod.rs", relative_prefix, mod_name, path::MAIN_SEPARATOR);
    let default_path = dir_path.join(&default_path_str);
    let secondary_path = dir_path.join(&secondary_path_str);
    let default_exists = sess.source_map().file_exists(&default_path);
    let secondary_exists = sess.source_map().file_exists(&secondary_path);

    let result = match (default_exists, secondary_exists) {
        (true, false) => Ok(ModulePathSuccess {
            path: default_path,
            directory_ownership: DirectoryOwnership::Owned { relative: Some(id) },
        }),
        (false, true) => Ok(ModulePathSuccess {
            path: secondary_path,
            directory_ownership: DirectoryOwnership::Owned { relative: None },
        }),
        (false, false) => {
            let mut err = struct_span_err!(
                sess.span_diagnostic,
                id.span,
                E0583,
                "file not found for module `{}`",
                mod_name,
            );
            err.help(&format!(
                "to create the module `{}`, create file \"{}\"",
                mod_name,
                default_path.display(),
            ));
            Err(err)
        }
        (true, true) => {
            let mut err = struct_span_err!(
                sess.span_diagnostic,
                id.span,
                E0584,
                "file for module `{}` found at both {} and {}",
                mod_name,
                default_path_str,
                secondary_path_str,
            );
            err.help("delete or rename one of them to remove the ambiguity");
            Err(err)
        }
    };

    ModulePath { name: mod_name, path_exists: default_exists || secondary_exists, result }
}
<|endoftext|>