<commit_before>//! Main evaluator loop and setting up the initial stack frame.

use rand::rngs::StdRng;
use rand::SeedableRng;

use rustc::hir::def_id::DefId;
use rustc::ty::layout::{Align, LayoutOf, Size};
use rustc::ty::{self, TyCtxt};
use syntax::source_map::DUMMY_SP;

use crate::{
    struct_error, EnvVars, Evaluator, FnVal, HelpersEvalContextExt, InterpCx, InterpError,
    InterpResult, MemoryExtra, MiriMemoryKind, Pointer, Scalar, StackPopCleanup, Tag,
    TlsEvalContextExt,
};

/// Configuration needed to spawn a Miri instance.
#[derive(Clone)]
pub struct MiriConfig {
    /// Determine if validity checking and Stacked Borrows are enabled.
    pub validate: bool,
    /// Determines if communication with the host environment is enabled.
    pub communicate: bool,
    /// Environment variables that should always be isolated from the host.
    pub excluded_env_vars: Vec<String>,
    /// Command-line arguments passed to the interpreted program.
    pub args: Vec<String>,
    /// The seed to use when non-determinism or randomness are required (e.g. ptr-to-int cast, `getrandom()`).
    pub seed: Option<u64>,
}

// Used by priroda.
pub fn create_ecx<'mir, 'tcx: 'mir>(
    tcx: TyCtxt<'tcx>,
    main_id: DefId,
    config: MiriConfig,
) -> InterpResult<'tcx, InterpCx<'mir, 'tcx, Evaluator<'tcx>>> {
    let mut ecx = InterpCx::new(
        tcx.at(syntax::source_map::DUMMY_SP),
        ty::ParamEnv::reveal_all(),
        Evaluator::new(config.communicate),
        MemoryExtra::new(
            StdRng::seed_from_u64(config.seed.unwrap_or(0)),
            config.validate,
        ),
    );
    // Complete initialization.
    EnvVars::init(&mut ecx, config.excluded_env_vars);

    // Setup first stack-frame
    let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);
    let main_mir = ecx.load_mir(main_instance.def, None)?;

    if !main_mir.return_ty().is_unit() || main_mir.arg_count != 0 {
        throw_unsup_format!("miri does not support main functions without `fn()` type signatures");
    }

    let start_id = tcx.lang_items().start_fn().unwrap();
    let main_ret_ty = tcx.fn_sig(main_id).output();
    let main_ret_ty = main_ret_ty.no_bound_vars().unwrap();
    let start_instance = ty::Instance::resolve(
        ecx.tcx.tcx,
        ty::ParamEnv::reveal_all(),
        start_id,
        ecx.tcx
            .mk_substs(::std::iter::once(ty::subst::GenericArg::from(main_ret_ty))),
    )
    .unwrap();
    let start_mir = ecx.load_mir(start_instance.def, None)?;

    if start_mir.arg_count != 3 {
        bug!(
            "'start' lang item should have three arguments, but has {}",
            start_mir.arg_count
        );
    }

    // Return value (in static memory so that it does not count as leak).
    let ret = ecx.layout_of(start_mir.return_ty())?;
    let ret_ptr = ecx.allocate(ret, MiriMemoryKind::Static.into());

    // Push our stack frame.
    ecx.push_stack_frame(
        start_instance,
        // There is no call site.
        DUMMY_SP,
        start_mir,
        Some(ret_ptr.into()),
        StackPopCleanup::None { cleanup: true },
    )?;

    let mut args = ecx.frame().body.args_iter();

    // First argument: pointer to `main()`.
    let main_ptr = ecx
        .memory
        .create_fn_alloc(FnVal::Instance(main_instance));
    let dest = ecx.local_place(args.next().unwrap())?;
    ecx.write_scalar(Scalar::Ptr(main_ptr), dest)?;

    // Second argument (argc): `1`.
    let dest = ecx.local_place(args.next().unwrap())?;
    let argc = Scalar::from_uint(config.args.len() as u128, dest.layout.size);
    ecx.write_scalar(argc, dest)?;
    // Store argc for macOS's `_NSGetArgc`.
    {
        let argc_place = ecx.allocate(dest.layout, MiriMemoryKind::Env.into());
        ecx.write_scalar(argc, argc_place.into())?;
        ecx.machine.argc = Some(argc_place.ptr.to_ptr()?);
    }

    // Third argument (`argv`): created from `config.args`.
    let dest = ecx.local_place(args.next().unwrap())?;
    // For Windows, construct a command string with all the aguments.
    let mut cmd = String::new();
    for arg in config.args.iter() {
        if !cmd.is_empty() {
            cmd.push(' ');
        }
        cmd.push_str(&*shell_escape::windows::escape(arg.as_str().into()));
    }
    // Don't forget `0` terminator.
    cmd.push(std::char::from_u32(0).unwrap());
    // Collect the pointers to the individual strings.
    let mut argvs = Vec::<Pointer<Tag>>::new();
    for arg in config.args {
        // Add `0` terminator.
        let mut arg = arg.into_bytes();
        arg.push(0);
        argvs.push(
            ecx.memory
                .allocate_static_bytes(arg.as_slice(), MiriMemoryKind::Static.into()),
        );
    }
    // Make an array with all these pointers, in the Miri memory.
    let argvs_layout = ecx.layout_of(
        ecx.tcx
            .mk_array(ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8), argvs.len() as u64),
    )?;
    let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Env.into());
    for (idx, arg) in argvs.into_iter().enumerate() {
        let place = ecx.mplace_field(argvs_place, idx as u64)?;
        ecx.write_scalar(Scalar::Ptr(arg), place.into())?;
    }
    ecx.memory
        .mark_immutable(argvs_place.ptr.assert_ptr().alloc_id)?;
    // Write a pointer to that place as the argument.
    let argv = argvs_place.ptr;
    ecx.write_scalar(argv, dest)?;
    // Store `argv` for macOS `_NSGetArgv`.
    {
        let argv_place = ecx.allocate(dest.layout, MiriMemoryKind::Env.into());
        ecx.write_scalar(argv, argv_place.into())?;
        ecx.machine.argv = Some(argv_place.ptr.to_ptr()?);
    }
    // Store command line as UTF-16 for Windows `GetCommandLineW`.
    {
        let cmd_utf16: Vec<u16> = cmd.encode_utf16().collect();
        let cmd_ptr = ecx.memory.allocate(
            Size::from_bytes(cmd_utf16.len() as u64 * 2),
            Align::from_bytes(2).unwrap(),
            MiriMemoryKind::Env.into(),
        );
        ecx.machine.cmd_line = Some(cmd_ptr);
        // Store the UTF-16 string. We just allocated so we know the bounds are fine.
        let char_size = Size::from_bytes(2);
        let cmd_alloc = ecx.memory.get_mut(cmd_ptr.alloc_id)?;
        let mut cur_ptr = cmd_ptr;
        for &c in cmd_utf16.iter() {
            cmd_alloc.write_scalar(
                &*ecx.tcx,
                cur_ptr,
                Scalar::from_uint(c, char_size).into(),
                char_size,
            )?;
            cur_ptr = cur_ptr.offset(char_size, &*ecx.tcx)?;
        }
    }

    args.next().expect_none("start lang item has more arguments than expected");

    // Set the last_error to 0
    let errno_layout = ecx.layout_of(ecx.tcx.types.u32)?;
    let errno_place = ecx.allocate(errno_layout, MiriMemoryKind::Static.into());
    ecx.write_scalar(Scalar::from_u32(0), errno_place.into())?;
    ecx.machine.last_error = Some(errno_place);

    Ok(ecx)
}

pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) {
    let mut ecx = match create_ecx(tcx, main_id, config) {
        Ok(ecx) => ecx,
        Err(mut err) => {
            err.print_backtrace();
            panic!("Miri initialziation error: {}", err.kind)
        }
    };

    // Perform the main execution.
    let res: InterpResult<'_> = (|| {
        ecx.run()?;
        ecx.run_tls_dtors()
    })();

    // Process the result.
    match res {
        Ok(()) => {
            let leaks = ecx.memory.leak_report();
            // Disable the leak test on some platforms where we do not
            // correctly implement TLS destructors.
            let target_os = ecx.tcx.tcx.sess.target.target.target_os.to_lowercase();
            let ignore_leaks = target_os == "windows" || target_os == "macos";
            if !ignore_leaks && leaks != 0 {
                tcx.sess.err("the evaluated program leaked memory");
            }
        }
        Err(mut e) => {
            // Special treatment for some error kinds
            let msg = match e.kind {
                InterpError::Exit(code) => std::process::exit(code),
                err_unsup!(NoMirFor(..)) =>
                    format!("{}. Did you set `MIRI_SYSROOT` to a Miri-enabled sysroot? You can prepare one with `cargo miri setup`.", e),
                _ => e.to_string()
            };
            e.print_backtrace();
            if let Some(frame) = ecx.stack().last() {
                let block = &frame.body.basic_blocks()[frame.block];
                let span = if frame.stmt < block.statements.len() {
                    block.statements[frame.stmt].source_info.span
                } else {
                    block.terminator().source_info.span
                };

                let msg = format!("Miri evaluation error: {}", msg);
                let mut err = struct_error(ecx.tcx.tcx.at(span), msg.as_str());
                let frames = ecx.generate_stacktrace(None);
                err.span_label(span, msg);
                // We iterate with indices because we need to look at the next frame (the caller).
                for idx in 0..frames.len() {
                    let frame_info = &frames[idx];
                    let call_site_is_local = frames.get(idx + 1).map_or(false, |caller_info| {
                        caller_info.instance.def_id().is_local()
                    });
                    if call_site_is_local {
                        err.span_note(frame_info.call_site, &frame_info.to_string());
                    } else {
                        err.note(&frame_info.to_string());
                    }
                }
                err.emit();
            } else {
                ecx.tcx.sess.err(&msg);
            }

            for (i, frame) in ecx.stack().iter().enumerate() {
                trace!("-------------------");
                trace!("Frame {}", i);
                trace!("    return: {:?}", frame.return_place.map(|p| *p));
                for (i, local) in frame.locals.iter().enumerate() {
                    trace!("    local {}: {:?}", i, local.value);
                }
            }
        }
    }
}
<commit_msg>Windows cmdline: avoid accessing allocations directly<commit_after>//! Main evaluator loop and setting up the initial stack frame.

use rand::rngs::StdRng;
use rand::SeedableRng;

use rustc::hir::def_id::DefId;
use rustc::ty::layout::{LayoutOf, Size};
use rustc::ty::{self, TyCtxt};
use syntax::source_map::DUMMY_SP;

use crate::{
    struct_error, EnvVars, Evaluator, FnVal, HelpersEvalContextExt, InterpCx, InterpError,
    InterpResult, MemoryExtra, MiriMemoryKind, Pointer, Scalar, StackPopCleanup, Tag,
    TlsEvalContextExt,
};

/// Configuration needed to spawn a Miri instance.
#[derive(Clone)]
pub struct MiriConfig {
    /// Determine if validity checking and Stacked Borrows are enabled.
    pub validate: bool,
    /// Determines if communication with the host environment is enabled.
    pub communicate: bool,
    /// Environment variables that should always be isolated from the host.
    pub excluded_env_vars: Vec<String>,
    /// Command-line arguments passed to the interpreted program.
    pub args: Vec<String>,
    /// The seed to use when non-determinism or randomness are required (e.g. ptr-to-int cast, `getrandom()`).
    pub seed: Option<u64>,
}

// Used by priroda.
pub fn create_ecx<'mir, 'tcx: 'mir>(
    tcx: TyCtxt<'tcx>,
    main_id: DefId,
    config: MiriConfig,
) -> InterpResult<'tcx, InterpCx<'mir, 'tcx, Evaluator<'tcx>>> {
    let mut ecx = InterpCx::new(
        tcx.at(syntax::source_map::DUMMY_SP),
        ty::ParamEnv::reveal_all(),
        Evaluator::new(config.communicate),
        MemoryExtra::new(
            StdRng::seed_from_u64(config.seed.unwrap_or(0)),
            config.validate,
        ),
    );
    // Complete initialization.
    EnvVars::init(&mut ecx, config.excluded_env_vars);

    // Setup first stack-frame
    let main_instance = ty::Instance::mono(tcx, main_id);
    let main_mir = ecx.load_mir(main_instance.def, None)?;

    if !main_mir.return_ty().is_unit() || main_mir.arg_count != 0 {
        throw_unsup_format!("miri does not support main functions without `fn()` type signatures");
    }

    let start_id = tcx.lang_items().start_fn().unwrap();
    let main_ret_ty = tcx.fn_sig(main_id).output();
    let main_ret_ty = main_ret_ty.no_bound_vars().unwrap();
    let start_instance = ty::Instance::resolve(
        tcx,
        ty::ParamEnv::reveal_all(),
        start_id,
        tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(main_ret_ty))),
    )
    .unwrap();
    let start_mir = ecx.load_mir(start_instance.def, None)?;

    if start_mir.arg_count != 3 {
        bug!(
            "'start' lang item should have three arguments, but has {}",
            start_mir.arg_count
        );
    }

    // Return value (in static memory so that it does not count as leak).
    let ret = ecx.layout_of(start_mir.return_ty())?;
    let ret_ptr = ecx.allocate(ret, MiriMemoryKind::Static.into());

    // Push our stack frame.
    ecx.push_stack_frame(
        start_instance,
        // There is no call site.
        DUMMY_SP,
        start_mir,
        Some(ret_ptr.into()),
        StackPopCleanup::None { cleanup: true },
    )?;

    let mut args = ecx.frame().body.args_iter();

    // First argument: pointer to `main()`.
    let main_ptr = ecx
        .memory
        .create_fn_alloc(FnVal::Instance(main_instance));
    let dest = ecx.local_place(args.next().unwrap())?;
    ecx.write_scalar(Scalar::Ptr(main_ptr), dest)?;

    // Second argument (argc): `1`.
    let dest = ecx.local_place(args.next().unwrap())?;
    let argc = Scalar::from_uint(config.args.len() as u128, dest.layout.size);
    ecx.write_scalar(argc, dest)?;
    // Store argc for macOS's `_NSGetArgc`.
    {
        let argc_place = ecx.allocate(dest.layout, MiriMemoryKind::Env.into());
        ecx.write_scalar(argc, argc_place.into())?;
        ecx.machine.argc = Some(argc_place.ptr.to_ptr()?);
    }

    // Third argument (`argv`): created from `config.args`.
    let dest = ecx.local_place(args.next().unwrap())?;
    // For Windows, construct a command string with all the aguments.
    let mut cmd = String::new();
    for arg in config.args.iter() {
        if !cmd.is_empty() {
            cmd.push(' ');
        }
        cmd.push_str(&*shell_escape::windows::escape(arg.as_str().into()));
    }
    // Don't forget `0` terminator.
    cmd.push(std::char::from_u32(0).unwrap());
    // Collect the pointers to the individual strings.
    let mut argvs = Vec::<Pointer<Tag>>::new();
    for arg in config.args {
        // Add `0` terminator.
        let mut arg = arg.into_bytes();
        arg.push(0);
        argvs.push(
            ecx.memory
                .allocate_static_bytes(arg.as_slice(), MiriMemoryKind::Static.into()),
        );
    }
    // Make an array with all these pointers, in the Miri memory.
    let argvs_layout = ecx.layout_of(
        tcx.mk_array(tcx.mk_imm_ptr(tcx.types.u8), argvs.len() as u64),
    )?;
    let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Env.into());
    for (idx, arg) in argvs.into_iter().enumerate() {
        let place = ecx.mplace_field(argvs_place, idx as u64)?;
        ecx.write_scalar(Scalar::Ptr(arg), place.into())?;
    }
    ecx.memory
        .mark_immutable(argvs_place.ptr.assert_ptr().alloc_id)?;
    // Write a pointer to that place as the argument.
    let argv = argvs_place.ptr;
    ecx.write_scalar(argv, dest)?;
    // Store `argv` for macOS `_NSGetArgv`.
    {
        let argv_place = ecx.allocate(dest.layout, MiriMemoryKind::Env.into());
        ecx.write_scalar(argv, argv_place.into())?;
        ecx.machine.argv = Some(argv_place.ptr.to_ptr()?);
    }
    // Store command line as UTF-16 for Windows `GetCommandLineW`.
    {
        let cmd_utf16: Vec<u16> = cmd.encode_utf16().collect();
        let cmd_type = tcx.mk_array(tcx.types.u16, cmd_utf16.len() as u64);
        let cmd_place = ecx.allocate(ecx.layout_of(cmd_type)?, MiriMemoryKind::Env.into());
        ecx.machine.cmd_line = Some(cmd_place.ptr.to_ptr()?);
        // Store the UTF-16 string. We just allocated so we know the bounds are fine.
        let char_size = Size::from_bytes(2);
        for (idx, &c) in cmd_utf16.iter().enumerate() {
            let place = ecx.mplace_field(cmd_place, idx as u64)?;
            ecx.write_scalar(Scalar::from_uint(c, char_size), place.into())?;
        }
    }

    args.next().expect_none("start lang item has more arguments than expected");

    // Set the last_error to 0
    let errno_layout = ecx.layout_of(tcx.types.u32)?;
    let errno_place = ecx.allocate(errno_layout, MiriMemoryKind::Static.into());
    ecx.write_scalar(Scalar::from_u32(0), errno_place.into())?;
    ecx.machine.last_error = Some(errno_place);

    Ok(ecx)
}

pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) {
    let mut ecx = match create_ecx(tcx, main_id, config) {
        Ok(ecx) => ecx,
        Err(mut err) => {
            err.print_backtrace();
            panic!("Miri initialziation error: {}", err.kind)
        }
    };

    // Perform the main execution.
    let res: InterpResult<'_> = (|| {
        ecx.run()?;
        ecx.run_tls_dtors()
    })();

    // Process the result.
    match res {
        Ok(()) => {
            let leaks = ecx.memory.leak_report();
            // Disable the leak test on some platforms where we do not
            // correctly implement TLS destructors.
            let target_os = ecx.tcx.tcx.sess.target.target.target_os.to_lowercase();
            let ignore_leaks = target_os == "windows" || target_os == "macos";
            if !ignore_leaks && leaks != 0 {
                tcx.sess.err("the evaluated program leaked memory");
            }
        }
        Err(mut e) => {
            // Special treatment for some error kinds
            let msg = match e.kind {
                InterpError::Exit(code) => std::process::exit(code),
                err_unsup!(NoMirFor(..)) =>
                    format!("{}. Did you set `MIRI_SYSROOT` to a Miri-enabled sysroot? You can prepare one with `cargo miri setup`.", e),
                _ => e.to_string()
            };
            e.print_backtrace();
            if let Some(frame) = ecx.stack().last() {
                let block = &frame.body.basic_blocks()[frame.block];
                let span = if frame.stmt < block.statements.len() {
                    block.statements[frame.stmt].source_info.span
                } else {
                    block.terminator().source_info.span
                };

                let msg = format!("Miri evaluation error: {}", msg);
                let mut err = struct_error(ecx.tcx.tcx.at(span), msg.as_str());
                let frames = ecx.generate_stacktrace(None);
                err.span_label(span, msg);
                // We iterate with indices because we need to look at the next frame (the caller).
                for idx in 0..frames.len() {
                    let frame_info = &frames[idx];
                    let call_site_is_local = frames.get(idx + 1).map_or(false, |caller_info| {
                        caller_info.instance.def_id().is_local()
                    });
                    if call_site_is_local {
                        err.span_note(frame_info.call_site, &frame_info.to_string());
                    } else {
                        err.note(&frame_info.to_string());
                    }
                }
                err.emit();
            } else {
                ecx.tcx.sess.err(&msg);
            }

            for (i, frame) in ecx.stack().iter().enumerate() {
                trace!("-------------------");
                trace!("Frame {}", i);
                trace!("    return: {:?}", frame.return_place.map(|p| *p));
                for (i, local) in frame.locals.iter().enumerate() {
                    trace!("    local {}: {:?}", i, local.value);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use ast::{Expr, ConstExpr, BlockExpr, TypeOfExpr, LocalExpr, GetConstFieldExpr, GetFieldExpr, CallExpr, WhileLoopExpr, IfExpr, SwitchExpr, ObjectDeclExpr, ArrayDeclExpr, FunctionDeclExpr, ArrowFunctionDeclExpr, NumOpExpr, BitOpExpr, LogOpExpr, CompOpExpr, ConstructExpr, ReturnExpr, ThrowExpr, AssignExpr};
use ast::{CNum, CInt, CString, CBool, CRegExp, CNull, CUndefined};
use ast::{OpSub, OpAdd, OpMul, OpDiv, OpMod};
use ast::{BitAnd, BitOr, BitXor, BitShl, BitShr};
use ast::{LogAnd, LogOr};
use ast::{CompEqual, CompNotEqual, CompStrictEqual, CompStrictNotEqual, CompGreaterThan, CompGreaterThanOrEqual, CompLessThan, CompLessThanOrEqual};
use js::value::{Value, ValueData, VNull, VUndefined, VString, VNumber, VInteger, VObject, VBoolean, VFunction, ResultValue, to_value, from_value};
use js::object::{INSTANCE_PROTOTYPE, PROTOTYPE, ObjectData};
use js::function::{RegularFunc, RegularFunction};
use js::{console, math, object, array, function, json, number, error, uri, string};
use collections::treemap::TreeMap;
use std::vec::Vec;
use std::gc::Gc;
use std::cell::RefCell;
use std::str::MaybeOwned;
/// An execution engine
pub trait Executor {
	/// Makes a new execution engine
	fn new() -> ~Self;
	/// Sets a global variable
	fn set_global(&mut self, name:MaybeOwned, val:Value) -> Value;
	/// Gets a global variable
	fn get_global(&self, name:MaybeOwned) -> Value;
	/// Make a new scope
	fn make_scope(&mut self) -> Gc<RefCell<ObjectData>>;
	/// Destroy the current scope
	fn destroy_scope(&mut self) -> ();
	/// Runs the expression
	fn run(&mut self, expr:&Expr) -> ResultValue;
}
#[deriving(Clone)]
/// An intepreter
pub struct Interpreter {
	/// An object representing the global variables
	global: Value,
	/// The scopes
	scopes: Vec<Gc<RefCell<ObjectData>>>,
}
impl Executor for Interpreter {
	fn new() -> ~Interpreter {
		let global = ValueData::new_obj(None);
		object::init(global);
		console::init(global);
		math::init(global);
		array::init(global);
		function::init(global);
		json::init(global);
		number::init(global);
		error::init(global);
		number::init(global);
		string::init(global);
		uri::init(global);
		return ~Interpreter {global: global, scopes: Vec::new()};
	}
	fn set_global(&mut self, name:MaybeOwned, val:Value) -> Value {
		self.global.borrow().set_field(name, val)
	}
	fn get_global(&self, name:MaybeOwned) -> Value {
		self.global.borrow().get_field(name)
	}
	fn make_scope(&mut self) -> Gc<RefCell<ObjectData>> {
		let value = Gc::new(RefCell::new(TreeMap::new()));
		self.scopes.push(value.clone());
		value
	}
	fn destroy_scope(&mut self) -> () {
		self.scopes.pop();
	}
	fn run(&mut self, expr:&Expr) -> ResultValue {
		match expr.def {
			ConstExpr(CNull) => Ok(Gc::new(VNull)),
			ConstExpr(CUndefined) => Ok(Gc::new(VUndefined)),
			ConstExpr(CNum(num)) => Ok(to_value(num)),
			ConstExpr(CInt(num)) => Ok(to_value(num)),
			ConstExpr(CString(ref str)) => Ok(Gc::new(VString(str.to_owned()))),
			ConstExpr(CBool(val)) => Ok(Gc::new(VBoolean(val))),
			ConstExpr(CRegExp(_, _, _)) => Ok(Gc::new(VNull)),
			BlockExpr(ref es) => {
				let mut obj = Gc::new(VNull);
				for e in es.iter() {
					let val = try!(self.run(*e));
					if e == es.last().unwrap() {
						obj = val;
					}
				}
				Ok(obj)
			},
			LocalExpr(ref name) => {
				let mut value = Gc::new(VUndefined);
				for scope in self.scopes.iter().rev() {
					match scope.borrow().borrow().find(name) {
						Some(v) => {
							value = v.value.clone();
							break;
						}
						None => ()
					}
				}
				Ok(if value.borrow().is_undefined() {
					self.global.borrow().get_field(name.clone().into_maybe_owned())
				} else {
					value
				})
			},
			GetConstFieldExpr(ref obj, ref field) => {
				let val_obj = try!(self.run(*obj));
				Ok(val_obj.borrow().get_field(field.clone().into_maybe_owned()))
			},
			GetFieldExpr(ref obj, ref field) => {
				let val_obj = try!(self.run(*obj));
				let val_field = try!(self.run(*field));
				Ok(val_obj.borrow().get_field(val_field.borrow().to_str().into_maybe_owned()))
			},
			CallExpr(ref callee, ref args) => {
				let (this, func) = match callee.def {
					GetConstFieldExpr(ref obj, ref field) => {
						let obj = try!(self.run(*obj));
						(obj, obj.borrow().get_field(field.clone().into_maybe_owned()))
					},
					GetFieldExpr(ref obj, ref field) => {
						let obj = try!(self.run(*obj));
						let field = try!(self.run(*field));
						(obj, obj.borrow().get_field(field.borrow().to_str().into_maybe_owned()))
					},
					_ => (self.global.clone(), try!(self.run(callee.clone())))
				};
				let mut v_args = Vec::with_capacity(args.len());
				for arg in args.iter() {
					v_args.push(try!(self.run(*arg)));
				}
				match *func.borrow() {
					VFunction(ref func) => {
						func.borrow().call(self, this, Gc::new(VNull), v_args)
					},
					_ => Err(Gc::new(VUndefined))
				}
			},
			WhileLoopExpr(ref cond, ref expr) => {
				let mut result = Gc::new(VUndefined);
				while try!(self.run(*cond)).borrow().is_true() {
					result = try!(self.run(*expr));
				}
				Ok(result)
			},
			IfExpr(ref cond, ref expr, None) => {
				Ok(if try!(self.run(*cond)).borrow().is_true() {
					try!(self.run(*expr))
				} else {
					Gc::new(VUndefined)
				})
			},
			IfExpr(ref cond, ref expr, Some(ref else_e)) => {
				Ok(if try!(self.run(*cond)).borrow().is_true() {
					try!(self.run(*expr))
				} else {
					try!(self.run(*else_e))
				})
			},
			SwitchExpr(ref val_e, ref vals, ref default) => {
				let val = try!(self.run(*val_e)).borrow().clone();
				let mut result = Gc::new(VNull);
				let mut matched = false;
				for tup in vals.iter() {
					let tup:&(~Expr, Vec<~Expr>) = tup;
					match *tup {
						(ref cond, ref block) if(val == *try!(self.run(*cond)).borrow()) => {
							matched = true;
							let last_expr = block.last().unwrap();
							for expr in block.iter() {
								let e_result = try!(self.run(*expr));
								if expr == last_expr {
									result = e_result;
								}
							}
						},
						_ => ()
					}
				}
				if !matched && default.is_some() {
					result = try!(self.run(*default.as_ref().unwrap()));
				}
				Ok(result)
			},
			ObjectDeclExpr(ref map) => {
				let obj = ValueData::new_obj(Some(self.global));
				for (key, val) in map.iter() {
					obj.borrow().set_field(key.clone().into_maybe_owned(), try!(self.run(val.clone())));
				}
				Ok(obj)
			},
			ArrayDeclExpr(ref arr) => {
				let arr_map = ValueData::new_obj(Some(self.global));
				let mut index : i32 = 0;
				for val in arr.iter() {
					let val = try!(self.run(val.clone()));
					arr_map.borrow().set_field(index.to_str().into_maybe_owned(), val);
					index += 1;
				}
				arr_map.borrow().set_field(INSTANCE_PROTOTYPE.into_maybe_owned(), self.get_global("Array".into_maybe_owned()).borrow().get_field(PROTOTYPE.into_maybe_owned()));
				arr_map.borrow().set_field("length".into_maybe_owned(), to_value(index));
				Ok(arr_map)
			},
			FunctionDeclExpr(ref name, ref args, ref expr) => {
				let function = RegularFunc(RegularFunction::new(*expr.clone(), args.clone()));
				let val = Gc::new(VFunction(RefCell::new(function)));
				if name.is_some() {
					self.global.borrow().set_field(name.clone().unwrap().into_maybe_owned(), val);
				}
				Ok(val)
			},
			ArrowFunctionDeclExpr(ref args, ref expr) => {
				let function = RegularFunc(RegularFunction::new(*expr.clone(), args.clone()));
				let val = Gc::new(VFunction(RefCell::new(function)));
				Ok(val)
			},
			NumOpExpr(ref op, ref a, ref b) => {
				let v_a = try!(self.run(*a)).borrow().clone();
				let v_b = try!(self.run(*b)).borrow().clone();
				Ok(Gc::new(match *op {
					OpAdd => v_a + v_b,
					OpSub => v_a - v_b,
					OpMul => v_a * v_b,
					OpDiv => v_a / v_b,
					OpMod => v_a % v_b
				}))
			},
			BitOpExpr(ref op, ref a, ref b) => {
				let v_a = try!(self.run(*a)).borrow().clone();
				let v_b = try!(self.run(*b)).borrow().clone();
				Ok(Gc::new(match *op {
					BitAnd => v_a & v_b,
					BitOr => v_a | v_b,
					BitXor => v_a ^ v_b,
					BitShl => v_a << v_b,
					BitShr => v_a >> v_b
				}))
			},
			CompOpExpr(ref op, ref a, ref b) => {
				let v_r_a = try!(self.run(*a));
				let v_r_b = try!(self.run(*b));
				let v_a = v_r_a.borrow();
				let v_b = v_r_b.borrow();
				Ok(to_value(match *op {
					CompEqual => v_r_a.ptr_eq(&v_r_b) || v_a == v_b,
					CompNotEqual => !v_r_a.ptr_eq(&v_r_b) || v_a != v_b,
					CompStrictEqual => v_r_a.ptr_eq(&v_r_b) || v_a == v_b,
					CompStrictNotEqual => !v_r_a.ptr_eq(&v_r_b) || v_a != v_b,
					CompGreaterThan => v_a.to_num() > v_b.to_num(),
					CompGreaterThanOrEqual => v_a.to_num() >= v_b.to_num(),
					CompLessThan => v_a.to_num() < v_b.to_num(),
					CompLessThanOrEqual => v_a.to_num() <= v_b.to_num()
				}))
			},
			LogOpExpr(ref op, ref a, ref b) => {
				let v_a = from_value::<bool>(try!(self.run(*a))).unwrap();
				let v_b = from_value::<bool>(try!(self.run(*b))).unwrap();
				Ok(match *op {
					LogAnd => to_value(v_a && v_b),
					LogOr => to_value(v_a || v_b)
				})
			},
			ConstructExpr(ref callee, ref args) => {
				let func = try!(self.run(*callee));
				let mut v_args = Vec::with_capacity(args.len());
				for arg in args.iter() {
					v_args.push(try!(self.run(*arg)));
				}
				let this = Gc::new(VObject(RefCell::new(TreeMap::new())));
				this.borrow().set_field(INSTANCE_PROTOTYPE.into_maybe_owned(), func.borrow().get_field(PROTOTYPE.into_maybe_owned()));
				Ok(match *func.borrow() {
					VFunction(ref func) => {
						try!(func.borrow().call(self, this, Gc::new(VNull), v_args));
						this
					},
					_ => Gc::new(VUndefined)
				})
			},
			ReturnExpr(ref ret) => {
				match *ret {
					Some(ref v) =>
						self.run(v.clone()),
					None => Ok(Gc::new(VUndefined))
				}
			},
			ThrowExpr(ref ex) => Err(try!(self.run(*ex))),
			AssignExpr(ref ref_e, ref val_e) => {
				let val = try!(self.run(*val_e));
				match ref_e.def {
					LocalExpr(ref name) => {
						self.global.borrow().set_field(name.clone().into_maybe_owned(), val);
					},
					GetConstFieldExpr(ref obj, ref field) => {
						let val_obj = try!(self.run(*obj));
						val_obj.borrow().set_field(field.clone().into_maybe_owned(), val);
					},
					_ => ()
				}
				Ok(val)
			},
			TypeOfExpr(ref val_e) => {
				let val = try!(self.run(*val_e));
				Ok(to_value(match *val.borrow() {
					VUndefined => "undefined",
					VNull | VObject(_) => "object",
					VBoolean(_) => "boolean",
					VNumber(_) | VInteger(_) => "number",
					VString(_) => "string",
					VFunction(_) => "function"
				}.into_maybe_owned()))
			}
		}
	}
}<commit_msg>Simplify arrow function declaration<commit_after>use ast::{Expr, ConstExpr, BlockExpr, TypeOfExpr, LocalExpr, GetConstFieldExpr, GetFieldExpr, CallExpr, WhileLoopExpr, IfExpr, SwitchExpr, ObjectDeclExpr, ArrayDeclExpr, FunctionDeclExpr, ArrowFunctionDeclExpr, NumOpExpr, BitOpExpr, LogOpExpr, CompOpExpr, ConstructExpr, ReturnExpr, ThrowExpr, AssignExpr};
use ast::{CNum, CInt, CString, CBool, CRegExp, CNull, CUndefined};
use ast::{OpSub, OpAdd, OpMul, OpDiv, OpMod};
use ast::{BitAnd, BitOr, BitXor, BitShl, BitShr};
use ast::{LogAnd, LogOr};
use ast::{CompEqual, CompNotEqual, CompStrictEqual, CompStrictNotEqual, CompGreaterThan, CompGreaterThanOrEqual, CompLessThan, CompLessThanOrEqual};
use js::value::{Value, ValueData, VNull, VUndefined, VString, VNumber, VInteger, VObject, VBoolean, VFunction, ResultValue, to_value, from_value};
use js::object::{INSTANCE_PROTOTYPE, PROTOTYPE, ObjectData};
use js::function::{RegularFunc, RegularFunction};
use js::{console, math, object, array, function, json, number, error, uri, string};
use collections::treemap::TreeMap;
use std::vec::Vec;
use std::gc::Gc;
use std::cell::RefCell;
use std::str::MaybeOwned;
/// An execution engine
pub trait Executor {
	/// Makes a new execution engine
	fn new() -> ~Self;
	/// Sets a global variable
	fn set_global(&mut self, name:MaybeOwned, val:Value) -> Value;
	/// Gets a global variable
	fn get_global(&self, name:MaybeOwned) -> Value;
	/// Make a new scope
	fn make_scope(&mut self) -> Gc<RefCell<ObjectData>>;
	/// Destroy the current scope
	fn destroy_scope(&mut self) -> ();
	/// Runs the expression
	fn run(&mut self, expr:&Expr) -> ResultValue;
}
#[deriving(Clone)]
/// An intepreter
pub struct Interpreter {
	/// An object representing the global variables
	global: Value,
	/// The scopes
	scopes: Vec<Gc<RefCell<ObjectData>>>,
}
impl Executor for Interpreter {
	fn new() -> ~Interpreter {
		let global = ValueData::new_obj(None);
		object::init(global);
		console::init(global);
		math::init(global);
		array::init(global);
		function::init(global);
		json::init(global);
		number::init(global);
		error::init(global);
		number::init(global);
		string::init(global);
		uri::init(global);
		return ~Interpreter {global: global, scopes: Vec::new()};
	}
	fn set_global(&mut self, name:MaybeOwned, val:Value) -> Value {
		self.global.borrow().set_field(name, val)
	}
	fn get_global(&self, name:MaybeOwned) -> Value {
		self.global.borrow().get_field(name)
	}
	fn make_scope(&mut self) -> Gc<RefCell<ObjectData>> {
		let value = Gc::new(RefCell::new(TreeMap::new()));
		self.scopes.push(value.clone());
		value
	}
	fn destroy_scope(&mut self) -> () {
		self.scopes.pop();
	}
	fn run(&mut self, expr:&Expr) -> ResultValue {
		match expr.def {
			ConstExpr(CNull) => Ok(Gc::new(VNull)),
			ConstExpr(CUndefined) => Ok(Gc::new(VUndefined)),
			ConstExpr(CNum(num)) => Ok(to_value(num)),
			ConstExpr(CInt(num)) => Ok(to_value(num)),
			ConstExpr(CString(ref str)) => Ok(Gc::new(VString(str.to_owned()))),
			ConstExpr(CBool(val)) => Ok(Gc::new(VBoolean(val))),
			ConstExpr(CRegExp(_, _, _)) => Ok(Gc::new(VNull)),
			BlockExpr(ref es) => {
				let mut obj = Gc::new(VNull);
				for e in es.iter() {
					let val = try!(self.run(*e));
					if e == es.last().unwrap() {
						obj = val;
					}
				}
				Ok(obj)
			},
			LocalExpr(ref name) => {
				let mut value = Gc::new(VUndefined);
				for scope in self.scopes.iter().rev() {
					match scope.borrow().borrow().find(name) {
						Some(v) => {
							value = v.value.clone();
							break;
						}
						None => ()
					}
				}
				Ok(if value.borrow().is_undefined() {
					self.global.borrow().get_field(name.clone().into_maybe_owned())
				} else {
					value
				})
			},
			GetConstFieldExpr(ref obj, ref field) => {
				let val_obj = try!(self.run(*obj));
				Ok(val_obj.borrow().get_field(field.clone().into_maybe_owned()))
			},
			GetFieldExpr(ref obj, ref field) => {
				let val_obj = try!(self.run(*obj));
				let val_field = try!(self.run(*field));
				Ok(val_obj.borrow().get_field(val_field.borrow().to_str().into_maybe_owned()))
			},
			CallExpr(ref callee, ref args) => {
				let (this, func) = match callee.def {
					GetConstFieldExpr(ref obj, ref field) => {
						let obj = try!(self.run(*obj));
						(obj, obj.borrow().get_field(field.clone().into_maybe_owned()))
					},
					GetFieldExpr(ref obj, ref field) => {
						let obj = try!(self.run(*obj));
						let field = try!(self.run(*field));
						(obj, obj.borrow().get_field(field.borrow().to_str().into_maybe_owned()))
					},
					_ => (self.global.clone(), try!(self.run(callee.clone())))
				};
				let mut v_args = Vec::with_capacity(args.len());
				for arg in args.iter() {
					v_args.push(try!(self.run(*arg)));
				}
				match *func.borrow() {
					VFunction(ref func) => {
						func.borrow().call(self, this, Gc::new(VNull), v_args)
					},
					_ => Err(Gc::new(VUndefined))
				}
			},
			WhileLoopExpr(ref cond, ref expr) => {
				let mut result = Gc::new(VUndefined);
				while try!(self.run(*cond)).borrow().is_true() {
					result = try!(self.run(*expr));
				}
				Ok(result)
			},
			IfExpr(ref cond, ref expr, None) => {
				Ok(if try!(self.run(*cond)).borrow().is_true() {
					try!(self.run(*expr))
				} else {
					Gc::new(VUndefined)
				})
			},
			IfExpr(ref cond, ref expr, Some(ref else_e)) => {
				Ok(if try!(self.run(*cond)).borrow().is_true() {
					try!(self.run(*expr))
				} else {
					try!(self.run(*else_e))
				})
			},
			SwitchExpr(ref val_e, ref vals, ref default) => {
				let val = try!(self.run(*val_e)).borrow().clone();
				let mut result = Gc::new(VNull);
				let mut matched = false;
				for tup in vals.iter() {
					let tup:&(~Expr, Vec<~Expr>) = tup;
					match *tup {
						(ref cond, ref block) if(val == *try!(self.run(*cond)).borrow()) => {
							matched = true;
							let last_expr = block.last().unwrap();
							for expr in block.iter() {
								let e_result = try!(self.run(*expr));
								if expr == last_expr {
									result = e_result;
								}
							}
						},
						_ => ()
					}
				}
				if !matched && default.is_some() {
					result = try!(self.run(*default.as_ref().unwrap()));
				}
				Ok(result)
			},
			ObjectDeclExpr(ref map) => {
				let obj = ValueData::new_obj(Some(self.global));
				for (key, val) in map.iter() {
					obj.borrow().set_field(key.clone().into_maybe_owned(), try!(self.run(val.clone())));
				}
				Ok(obj)
			},
			ArrayDeclExpr(ref arr) => {
				let arr_map = ValueData::new_obj(Some(self.global));
				let mut index : i32 = 0;
				for val in arr.iter() {
					let val = try!(self.run(val.clone()));
					arr_map.borrow().set_field(index.to_str().into_maybe_owned(), val);
					index += 1;
				}
				arr_map.borrow().set_field(INSTANCE_PROTOTYPE.into_maybe_owned(), self.get_global("Array".into_maybe_owned()).borrow().get_field(PROTOTYPE.into_maybe_owned()));
				arr_map.borrow().set_field("length".into_maybe_owned(), to_value(index));
				Ok(arr_map)
			},
			FunctionDeclExpr(ref name, ref args, ref expr) => {
				let function = RegularFunc(RegularFunction::new(*expr.clone(), args.clone()));
				let val = Gc::new(VFunction(RefCell::new(function)));
				if name.is_some() {
					self.global.borrow().set_field(name.clone().unwrap().into_maybe_owned(), val);
				}
				Ok(val)
			},
			ArrowFunctionDeclExpr(ref args, ref expr) => {
				let function = RegularFunc(RegularFunction::new(*expr.clone(), args.clone()));
				Ok(Gc::new(VFunction(RefCell::new(function))))
			},
			NumOpExpr(ref op, ref a, ref b) => {
				let v_a = try!(self.run(*a)).borrow().clone();
				let v_b = try!(self.run(*b)).borrow().clone();
				Ok(Gc::new(match *op {
					OpAdd => v_a + v_b,
					OpSub => v_a - v_b,
					OpMul => v_a * v_b,
					OpDiv => v_a / v_b,
					OpMod => v_a % v_b
				}))
			},
			BitOpExpr(ref op, ref a, ref b) => {
				let v_a = try!(self.run(*a)).borrow().clone();
				let v_b = try!(self.run(*b)).borrow().clone();
				Ok(Gc::new(match *op {
					BitAnd => v_a & v_b,
					BitOr => v_a | v_b,
					BitXor => v_a ^ v_b,
					BitShl => v_a << v_b,
					BitShr => v_a >> v_b
				}))
			},
			CompOpExpr(ref op, ref a, ref b) => {
				let v_r_a = try!(self.run(*a));
				let v_r_b = try!(self.run(*b));
				let v_a = v_r_a.borrow();
				let v_b = v_r_b.borrow();
				Ok(to_value(match *op {
					CompEqual => v_r_a.ptr_eq(&v_r_b) || v_a == v_b,
					CompNotEqual => !v_r_a.ptr_eq(&v_r_b) || v_a != v_b,
					CompStrictEqual => v_r_a.ptr_eq(&v_r_b) || v_a == v_b,
					CompStrictNotEqual => !v_r_a.ptr_eq(&v_r_b) || v_a != v_b,
					CompGreaterThan => v_a.to_num() > v_b.to_num(),
					CompGreaterThanOrEqual => v_a.to_num() >= v_b.to_num(),
					CompLessThan => v_a.to_num() < v_b.to_num(),
					CompLessThanOrEqual => v_a.to_num() <= v_b.to_num()
				}))
			},
			LogOpExpr(ref op, ref a, ref b) => {
				let v_a = from_value::<bool>(try!(self.run(*a))).unwrap();
				let v_b = from_value::<bool>(try!(self.run(*b))).unwrap();
				Ok(match *op {
					LogAnd => to_value(v_a && v_b),
					LogOr => to_value(v_a || v_b)
				})
			},
			ConstructExpr(ref callee, ref args) => {
				let func = try!(self.run(*callee));
				let mut v_args = Vec::with_capacity(args.len());
				for arg in args.iter() {
					v_args.push(try!(self.run(*arg)));
				}
				let this = Gc::new(VObject(RefCell::new(TreeMap::new())));
				this.borrow().set_field(INSTANCE_PROTOTYPE.into_maybe_owned(), func.borrow().get_field(PROTOTYPE.into_maybe_owned()));
				Ok(match *func.borrow() {
					VFunction(ref func) => {
						try!(func.borrow().call(self, this, Gc::new(VNull), v_args));
						this
					},
					_ => Gc::new(VUndefined)
				})
			},
			ReturnExpr(ref ret) => {
				match *ret {
					Some(ref v) =>
						self.run(v.clone()),
					None => Ok(Gc::new(VUndefined))
				}
			},
			ThrowExpr(ref ex) => Err(try!(self.run(*ex))),
			AssignExpr(ref ref_e, ref val_e) => {
				let val = try!(self.run(*val_e));
				match ref_e.def {
					LocalExpr(ref name) => {
						self.global.borrow().set_field(name.clone().into_maybe_owned(), val);
					},
					GetConstFieldExpr(ref obj, ref field) => {
						let val_obj = try!(self.run(*obj));
						val_obj.borrow().set_field(field.clone().into_maybe_owned(), val);
					},
					_ => ()
				}
				Ok(val)
			},
			TypeOfExpr(ref val_e) => {
				let val = try!(self.run(*val_e));
				Ok(to_value(match *val.borrow() {
					VUndefined => "undefined",
					VNull | VObject(_) => "object",
					VBoolean(_) => "boolean",
					VNumber(_) | VInteger(_) => "number",
					VString(_) => "string",
					VFunction(_) => "function"
				}.into_maybe_owned()))
			}
		}
	}
}<|endoftext|>


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

use quote::{Tokens, ToTokens};
use syn::{Body, Field, Ident, Variant, VariantData, MacroInput, Ty};
use utils::{numbered_vars, number_idents};

/// Provides the hook to expand `#[derive(From)]` into an implementation of `From`
pub fn expand(input: &MacroInput, _: &str) -> Tokens {
    let input_type = &input.ident;
    match input.body {
        Body::Struct(VariantData::Tuple(ref fields)) => {
            if fields.len() == 1 {
                newtype_from(input_type, &fields[0].ty)
            }
            else {
                tuple_from(input_type, fields)
            }
        }
        Body::Struct(VariantData::Struct(ref fields)) => {
            if fields.len() == 1 {
                newtype_struct_from(input_type, &fields[0])
            }
            else {
                struct_from(input_type, fields)
            }
        }
        Body::Enum(ref variants) => {
            enum_from(input_type, variants)
        }
        _ => panic!("Only tuple structs and enums can derive From")
    }
}

fn newtype_from(input_type: &Ident, original_type: &Ty) -> Tokens {
    quote!{
        impl ::std::convert::From<#original_type> for #input_type {
            fn from(orig: #original_type) -> #input_type {
                #input_type(orig)
            }
        }
    }
}

fn newtype_struct_from(input_type: &Ident, field: &Field) -> Tokens {
    let field_name = &field.ident;
    let field_ty = &field.ty;
    quote!{
        impl ::std::convert::From<#field_ty> for #input_type {
            fn from(orig: #field_ty) -> #input_type {
                #input_type{#field_name: orig}
            }
        }
    }
}

fn tuple_from<T: ToTokens>(input_type: &T, fields: &Vec<Field>) -> Tokens {
    let field_names = &number_idents(fields.len());
    let types: &Vec<_> = &fields.iter().map(|f| &f.ty).collect();
    quote!{
        impl ::std::convert::From<(#(#types),*)> for #input_type {
            fn from(origin: (#(#types),*)) -> #input_type {
                #input_type(#(origin.#field_names),*)
            }
        }
    }
}

fn struct_from<T: ToTokens>(input_type: &T, fields: &Vec<Field>) -> Tokens {
    let argument_field_names = &number_idents(fields.len());
    let types: &Vec<_> = &fields.iter().map(|f| &f.ty).collect();
    let field_names: &Vec<_> = &fields.iter().map(|f| f.ident.as_ref().unwrap()).collect();
    quote!{
        impl ::std::convert::From<(#(#types),*)> for #input_type {
            fn from(origin: (#(#types),*)) -> #input_type {
                #input_type{#(#field_names: origin.#argument_field_names),*}
            }
        }
    }
}

fn enum_from(enum_ident: &Ident, variants: &Vec<Variant>) -> Tokens {
    let mut types = vec![];
    let mut idents = vec![];
    let mut type_counts = HashMap::new();

    for variant in variants {
        match variant.data {
            VariantData::Tuple(ref structs) => {
                if structs.len() == 1 {
                    let ty = &structs[0].ty;
                    idents.push(&variant.ident);
                    types.push(ty);
                    let counter = type_counts.entry(ty).or_insert(0);
                    *counter += 1;
                }
            }
            _ => {},
        }
    }

    let mut tokens = Tokens::new();

    for (ident, old_type) in idents.iter().zip(types) {
        if *type_counts.get(&old_type).unwrap() != 1 {
            // If more than one newtype is present don't add automatic From, since it is
            // ambiguous.
            continue
        }

        tokens.append(&quote!(
            impl ::std::convert::From<#old_type> for #enum_ident {
                fn from(a: #old_type) -> #enum_ident {
                    #enum_ident::#ident(a)
                }
            }
        ).to_string())
    }
    tokens
}

<commit_msg>From: Use original as name for argument<commit_after>use std::collections::HashMap;

use quote::{Tokens, ToTokens};
use syn::{Body, Field, Ident, Variant, VariantData, MacroInput, Ty};
use utils::{numbered_vars, number_idents};

/// Provides the hook to expand `#[derive(From)]` into an implementation of `From`
pub fn expand(input: &MacroInput, _: &str) -> Tokens {
    let input_type = &input.ident;
    match input.body {
        Body::Struct(VariantData::Tuple(ref fields)) => {
            if fields.len() == 1 {
                newtype_from(input_type, &fields[0].ty)
            }
            else {
                tuple_from(input_type, fields)
            }
        }
        Body::Struct(VariantData::Struct(ref fields)) => {
            if fields.len() == 1 {
                newtype_struct_from(input_type, &fields[0])
            }
            else {
                struct_from(input_type, fields)
            }
        }
        Body::Enum(ref variants) => {
            enum_from(input_type, variants)
        }
        _ => panic!("Only tuple structs and enums can derive From")
    }
}

fn newtype_from(input_type: &Ident, original_type: &Ty) -> Tokens {
    quote!{
        impl ::std::convert::From<#original_type> for #input_type {
            fn from(original: #original_type) -> #input_type {
                #input_type(original)
            }
        }
    }
}

fn newtype_struct_from(input_type: &Ident, field: &Field) -> Tokens {
    let field_name = &field.ident;
    let field_ty = &field.ty;
    quote!{
        impl ::std::convert::From<#field_ty> for #input_type {
            fn from(original: #field_ty) -> #input_type {
                #input_type{#field_name: original}
            }
        }
    }
}

fn tuple_from<T: ToTokens>(input_type: &T, fields: &Vec<Field>) -> Tokens {
    let field_names = &number_idents(fields.len());
    let types: &Vec<_> = &fields.iter().map(|f| &f.ty).collect();
    quote!{
        impl ::std::convert::From<(#(#types),*)> for #input_type {
            fn from(original: (#(#types),*)) -> #input_type {
                #input_type(#(original.#field_names),*)
            }
        }
    }
}

fn struct_from<T: ToTokens>(input_type: &T, fields: &Vec<Field>) -> Tokens {
    let argument_field_names = &number_idents(fields.len());
    let types: &Vec<_> = &fields.iter().map(|f| &f.ty).collect();
    let field_names: &Vec<_> = &fields.iter().map(|f| f.ident.as_ref().unwrap()).collect();
    quote!{
        impl ::std::convert::From<(#(#types),*)> for #input_type {
            fn from(original: (#(#types),*)) -> #input_type {
                #input_type{#(#field_names: original.#argument_field_names),*}
            }
        }
    }
}

fn enum_from(enum_ident: &Ident, variants: &Vec<Variant>) -> Tokens {
    let mut types = vec![];
    let mut idents = vec![];
    let mut type_counts = HashMap::new();

    for variant in variants {
        match variant.data {
            VariantData::Tuple(ref structs) => {
                if structs.len() == 1 {
                    let ty = &structs[0].ty;
                    idents.push(&variant.ident);
                    types.push(ty);
                    let counter = type_counts.entry(ty).or_insert(0);
                    *counter += 1;
                }
            }
            _ => {},
        }
    }

    let mut tokens = Tokens::new();

    for (ident, old_type) in idents.iter().zip(types) {
        if *type_counts.get(&old_type).unwrap() != 1 {
            // If more than one newtype is present don't add automatic From, since it is
            // ambiguous.
            continue
        }

        tokens.append(&quote!(
            impl ::std::convert::From<#old_type> for #enum_ident {
                fn from(original: #old_type) -> #enum_ident {
                    #enum_ident::#ident(original)
                }
            }
        ).to_string())
    }
    tokens
}

<|endoftext|>


<commit_before>//!Defines the game state and logic

use crate::dictionary::CONF_LINE_SECRET_MODIFIER__VISIBLE;
use std::fmt;

/// Defines the line-break position when displaying the secret string.
const LINE_WIDTH: usize = 20;

/// One character of the secret string.
#[derive(Debug, Clone, PartialEq)]
struct HangmanChar {
    char_: char,
    visible: bool,
}

/// Format HangmanChar.
impl fmt::Display for HangmanChar {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        if self.visible {
            write!(f, "{}", self.char_)
        } else {
            write!(f, "_")
        }
    }
}

/// A subset of the game state. Can be derived from `Game` struct.
#[derive(PartialEq)]
pub enum State {
    /// The game is ongoing.
    Ongoing,
    /// The player won.
    Victory,
    /// The player lost.
    Defeat,
}

/// The game state.
#[derive(Debug, PartialEq)]
pub struct Game {
    secret: Vec<HangmanChar>,
    pub lives: u8,
    pub last_guess: char,
}

impl Game {
    /// Derive State from Game data.
    pub fn get_state(&self) -> State {
        if self.lives == 0 {
            State::Defeat
        } else if self.secret.iter().all(|c| c.visible) {
            State::Victory
        } else {
            State::Ongoing
        }
    }

    /// Constructor.
    pub fn new(secretstr: &str, lives: u8) -> Self {
        // parse `secretsstr`, flip 'visible' every CONF_LINE_SECRET_MODIFIER__VISIBLE
        let w = secretstr
            .chars()
            // for every * found flip v_acc
            .scan(false, |v_acc, c| {
                *v_acc ^= c == CONF_LINE_SECRET_MODIFIER__VISIBLE;
                if c == CONF_LINE_SECRET_MODIFIER__VISIBLE {
                    Some(None)
                } else {
                    Some(Some(HangmanChar {
                        char_: c,
                        visible: *v_acc,
                    }))
                }
            })
            // omit None and unwrap
            .filter_map(|s| s)
            //.inspect(|ref x| println!("after scan:\t{:?}", x))
            .collect();

        Self {
            secret: w,
            lives,
            last_guess: ' ',
        }
    }

    /// Process a guess and modify the game state.
    pub fn guess(&mut self, char_: char) {
        if char_ == '\n' {
            return;
        };
        self.last_guess = char_;
        let mut found = false;
        for h_char in &mut self.secret {
            if h_char.char_.eq_ignore_ascii_case(&char_) {
                h_char.visible = true;
                found = true;
            }
        }

        if !found {
            self.lives -= 1;
        }

        if self.lives == 0 {
            for hc in &mut self.secret {
                hc.visible = true;
            }
        }
    }

    /// The number of disclosed characters of the secret.
    pub fn visible_chars(&self) -> usize {
        self.secret.iter().filter(|hc| !hc.visible).count()
    }
}

impl fmt::Display for Game {
    /// Graphical representation of the game state.
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        writeln!(
            f,
            "Lives:\t{}\tLast guess: {}\n",
            self.lives, self.last_guess
        )?;

        let mut linebreak = false;
        for (n, c) in self.secret.iter().enumerate() {
            if n % LINE_WIDTH == 0 {
                linebreak = true
            };
            if n == 0 {
                linebreak = false
            };
            if linebreak && (c.char_ == ' ') {
                linebreak = false;
                writeln!(f, " {}", c)?
            } else {
                write!(f, " {}", c)?
            }
        }
        writeln!(f)
    }
}
// ***********************

#[cfg(test)]
mod tests {
    use super::*;

    /// Play simulation
    #[test]
    fn test_game_simulation() {
        let mut game = Game::new("_ab _cd", 2);

        //println!("{:?}",game);
        let expected = Game {
            secret: [
                HangmanChar {
                    char_: 'a',
                    visible: true,
                },
                HangmanChar {
                    char_: 'b',
                    visible: true,
                },
                HangmanChar {
                    char_: ' ',
                    visible: true,
                },
                HangmanChar {
                    char_: 'c',
                    visible: false,
                },
                HangmanChar {
                    char_: 'd',
                    visible: false,
                },
            ]
            .to_vec(),
            lives: 2,
            last_guess: ' ',
        };

        assert!(game == expected);
        assert!(game.get_state() == State::Ongoing);

        // now we guess right
        game.guess('c');
        //println!("{:?}",game);
        let expected = Game {
            secret: [
                HangmanChar {
                    char_: 'a',
                    visible: true,
                },
                HangmanChar {
                    char_: 'b',
                    visible: true,
                },
                HangmanChar {
                    char_: ' ',
                    visible: true,
                },
                HangmanChar {
                    char_: 'c',
                    visible: true,
                },
                HangmanChar {
                    char_: 'd',
                    visible: false,
                },
            ]
            .to_vec(),
            lives: 2,
            last_guess: 'c',
        };

        assert!(game == expected);
        assert!(game.get_state() == State::Ongoing);

        // now we guess wrong
        game.guess('x');
        //println!("{:?}",game);
        let expected = Game {
            secret: [
                HangmanChar {
                    char_: 'a',
                    visible: true,
                },
                HangmanChar {
                    char_: 'b',
                    visible: true,
                },
                HangmanChar {
                    char_: ' ',
                    visible: true,
                },
                HangmanChar {
                    char_: 'c',
                    visible: true,
                },
                HangmanChar {
                    char_: 'd',
                    visible: false,
                },
            ]
            .to_vec(),
            lives: 1,
            last_guess: 'x',
        };

        assert!(game == expected);
        assert!(game.get_state() == State::Ongoing);

        // we guess wrong again and we loose
        game.guess('y');
        //println!("{:?}",game);
        let expected = Game {
            secret: [
                HangmanChar {
                    char_: 'a',
                    visible: true,
                },
                HangmanChar {
                    char_: 'b',
                    visible: true,
                },
                HangmanChar {
                    char_: ' ',
                    visible: true,
                },
                HangmanChar {
                    char_: 'c',
                    visible: true,
                },
                HangmanChar {
                    char_: 'd',
                    visible: true,
                },
            ]
            .to_vec(),
            lives: 0,
            last_guess: 'y',
        };

        assert!(game == expected);
        assert!(game.get_state() == State::Defeat);
    }
}
<commit_msg>refactor code, introduce Secret trait<commit_after>//!Defines the game state and logic

use crate::dictionary::CONF_LINE_SECRET_MODIFIER__VISIBLE;
use std::fmt;

/// Defines the line-break position when displaying the secret string.
const LINE_WIDTH: usize = 20;

/// One character of the secret string.
#[derive(Debug, Clone, PartialEq)]
struct HangmanChar {
    char_: char,
    visible: bool,
}

/// Format HangmanChar.
impl fmt::Display for HangmanChar {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        if self.visible {
            write!(f, "{}", self.char_)
        } else {
            write!(f, "_")
        }
    }
}

/// A subset of the game state. Can be derived from `Game` struct.
#[derive(PartialEq)]
pub enum State {
    /// The game is ongoing.
    Ongoing,
    /// The player won.
    Victory,
    /// The player lost.
    Defeat,
}

/// The secret
#[derive(Debug, PartialEq)]
pub struct Secret {
    chars: Vec<HangmanChar>,
}

impl Secret {
    /// Constructor.
    pub fn new(secretstr: &str) -> Self {
        // parse `secretsstr`, flip 'visible' every CONF_LINE_SECRET_MODIFIER__VISIBLE
        let w: Vec<HangmanChar> = secretstr
            .chars()
            // for every * found flip v_acc
            .scan(false, |v_acc, c| {
                *v_acc ^= c == CONF_LINE_SECRET_MODIFIER__VISIBLE;
                if c == CONF_LINE_SECRET_MODIFIER__VISIBLE {
                    Some(None)
                } else {
                    Some(Some(HangmanChar {
                        char_: c,
                        visible: *v_acc,
                    }))
                }
            })
            // omit None and unwrap
            .filter_map(|s| s)
            //.inspect(|ref x| println!("after scan:\t{:?}", x))
            .collect();

        Self { chars: w }
    }
}

impl fmt::Display for Secret {
    /// Graphical representation of the game state.
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mut linebreak = false;
        for (n, c) in self.chars.iter().enumerate() {
            if n % LINE_WIDTH == 0 {
                linebreak = true
            };
            if n == 0 {
                linebreak = false
            };
            if linebreak && (c.char_ == ' ') {
                linebreak = false;
                writeln!(f, " {}", c)?
            } else {
                write!(f, " {}", c)?
            }
        }
        writeln!(f)
    }
}

/// The game state.
#[derive(Debug, PartialEq)]
pub struct Game {
    secret: Secret,
    pub lives: u8,
    pub last_guess: char,
}

impl Game {
    /// Derive State from Game data.
    pub fn get_state(&self) -> State {
        if self.lives == 0 {
            State::Defeat
        } else if self.secret.chars.iter().all(|c| c.visible) {
            State::Victory
        } else {
            State::Ongoing
        }
    }

    /// Constructor.
    pub fn new(secretstr: &str, lives: u8) -> Self {
        // parse `secretsstr`, flip 'visible' every CONF_LINE_SECRET_MODIFIER__VISIBLE
        let secret = Secret::new(secretstr);

        Self {
            secret,
            lives,
            last_guess: ' ',
        }
    }

    /// Process a guess and modify the game state.
    pub fn guess(&mut self, char_: char) {
        if char_ == '\n' {
            return;
        };
        self.last_guess = char_;
        let mut found = false;
        for h_char in &mut self.secret.chars {
            if h_char.char_.eq_ignore_ascii_case(&char_) {
                h_char.visible = true;
                found = true;
            }
        }

        if !found {
            self.lives -= 1;
        }

        if self.lives == 0 {
            for hc in &mut self.secret.chars {
                hc.visible = true;
            }
        }
    }

    /// The number of disclosed characters of the secret.
    pub fn visible_chars(&self) -> usize {
        self.secret.chars.iter().filter(|hc| !hc.visible).count()
    }
}

impl fmt::Display for Game {
    /// Graphical representation of the game state.
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        writeln!(
            f,
            "Lives:\t{}\tLast guess: {}\n",
            self.lives, self.last_guess
        )?;

        write!(f, "{}", self.secret)
    }
}
// ***********************

#[cfg(test)]
mod tests {
    use super::*;

    /// Play simulation
    #[test]
    fn test_game_simulation() {
        let mut game = Game::new("_ab _cd", 2);

        //println!("{:?}",game);
        let expected = Game {
            secret: Secret {
                chars: [
                    HangmanChar {
                        char_: 'a',
                        visible: true,
                    },
                    HangmanChar {
                        char_: 'b',
                        visible: true,
                    },
                    HangmanChar {
                        char_: ' ',
                        visible: true,
                    },
                    HangmanChar {
                        char_: 'c',
                        visible: false,
                    },
                    HangmanChar {
                        char_: 'd',
                        visible: false,
                    },
                ]
                .to_vec(),
            },
            lives: 2,
            last_guess: ' ',
        };

        assert!(game == expected);
        assert!(game.get_state() == State::Ongoing);

        // now we guess right
        game.guess('c');
        //println!("{:?}",game);
        let expected = Game {
            secret: Secret {
                chars: [
                    HangmanChar {
                        char_: 'a',
                        visible: true,
                    },
                    HangmanChar {
                        char_: 'b',
                        visible: true,
                    },
                    HangmanChar {
                        char_: ' ',
                        visible: true,
                    },
                    HangmanChar {
                        char_: 'c',
                        visible: true,
                    },
                    HangmanChar {
                        char_: 'd',
                        visible: false,
                    },
                ]
                .to_vec(),
            },
            lives: 2,
            last_guess: 'c',
        };

        assert!(game == expected);
        assert!(game.get_state() == State::Ongoing);

        // now we guess wrong
        game.guess('x');
        //println!("{:?}",game);
        let expected = Game {
            secret: Secret {
                chars: [
                    HangmanChar {
                        char_: 'a',
                        visible: true,
                    },
                    HangmanChar {
                        char_: 'b',
                        visible: true,
                    },
                    HangmanChar {
                        char_: ' ',
                        visible: true,
                    },
                    HangmanChar {
                        char_: 'c',
                        visible: true,
                    },
                    HangmanChar {
                        char_: 'd',
                        visible: false,
                    },
                ]
                .to_vec(),
            },
            lives: 1,
            last_guess: 'x',
        };

        assert!(game == expected);
        assert!(game.get_state() == State::Ongoing);

        // we guess wrong again and we loose
        game.guess('y');
        //println!("{:?}",game);
        let expected = Game {
            secret: Secret {
                chars: [
                    HangmanChar {
                        char_: 'a',
                        visible: true,
                    },
                    HangmanChar {
                        char_: 'b',
                        visible: true,
                    },
                    HangmanChar {
                        char_: ' ',
                        visible: true,
                    },
                    HangmanChar {
                        char_: 'c',
                        visible: true,
                    },
                    HangmanChar {
                        char_: 'd',
                        visible: true,
                    },
                ]
                .to_vec(),
            },
            lives: 0,
            last_guess: 'y',
        };

        assert!(game == expected);
        assert!(game.get_state() == State::Defeat);
    }
}
<|endoftext|>


<commit_before>use std::str::CharIndices;
use std::io::Result as IoResult;
use std::io::Write;

use super::action::{IFlagAction, ParseResult};
use super::action::ParseResult::Help;

pub struct HelpAction;

impl IFlagAction for HelpAction {
    fn parse_flag(&self) -> ParseResult {
        return Help;
    }
}

struct WordsIter<'a> {
    data: &'a str,
    iter: CharIndices<'a>,
}

impl<'a> WordsIter<'a> {
    fn new(data: &'a str) -> WordsIter<'a> {
        return WordsIter {
            data: data,
            iter: data.char_indices(),
            };
    }
}

impl<'a> Iterator for WordsIter<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        let word_start;
        loop {
            let (idx, ch) = match self.iter.next() {
                None => return None,
                Some((idx, ch)) => ((idx, ch)),
            };
            match ch {
                ' ' | '\t' | '\r' | '\n' => continue,
                _ => {
                    word_start = idx;
                    break;
                }
            }
        }
        loop {
            let (idx, ch) = match self.iter.next() {
                None => break,
                Some((idx, ch)) => ((idx, ch)),
            };
            match ch {
                ' ' | '\t' | '\r' | '\n' => {
                    return Some(&self.data[word_start..idx]);
                }
                _ => continue,
            }
        }
        return Some(&self.data[word_start..self.data.len()]);
    }
}

pub fn wrap_text(buf: &mut Write, data: &str, width: usize, indent: usize)
    -> IoResult<()>
{
    let mut witer = WordsIter::new(data);
    let mut off = indent;
    match witer.next() {
        None => {
            return Ok(());
        }
        Some(word) => {
            try!(buf.write(word.as_bytes()));
            off += word.len();
        }
    }
    for word in witer {
        if off + word.len() + 1 > width {
            try!(buf.write(b"\n"));
            for _ in 0..indent {
                try!(buf.write(b" "));
            }
            off = indent;
        } else {
            try!(buf.write(b" "));
            off += 1;
        }
        try!(buf.write(word.as_bytes()));
        off += word.len();
    }
    return Ok(());
}
<commit_msg>Fix clippy reported problems of unused_io_amount lint<commit_after>use std::str::CharIndices;
use std::io::Result as IoResult;
use std::io::Write;

use super::action::{IFlagAction, ParseResult};
use super::action::ParseResult::Help;

pub struct HelpAction;

impl IFlagAction for HelpAction {
    fn parse_flag(&self) -> ParseResult {
        return Help;
    }
}

struct WordsIter<'a> {
    data: &'a str,
    iter: CharIndices<'a>,
}

impl<'a> WordsIter<'a> {
    fn new(data: &'a str) -> WordsIter<'a> {
        return WordsIter {
            data: data,
            iter: data.char_indices(),
            };
    }
}

impl<'a> Iterator for WordsIter<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        let word_start;
        loop {
            let (idx, ch) = match self.iter.next() {
                None => return None,
                Some((idx, ch)) => ((idx, ch)),
            };
            match ch {
                ' ' | '\t' | '\r' | '\n' => continue,
                _ => {
                    word_start = idx;
                    break;
                }
            }
        }
        loop {
            let (idx, ch) = match self.iter.next() {
                None => break,
                Some((idx, ch)) => ((idx, ch)),
            };
            match ch {
                ' ' | '\t' | '\r' | '\n' => {
                    return Some(&self.data[word_start..idx]);
                }
                _ => continue,
            }
        }
        return Some(&self.data[word_start..self.data.len()]);
    }
}

pub fn wrap_text(buf: &mut Write, data: &str, width: usize, indent: usize)
    -> IoResult<()>
{
    let mut witer = WordsIter::new(data);
    let mut off = indent;
    match witer.next() {
        None => {
            return Ok(());
        }
        Some(word) => {
            try!(buf.write_all(word.as_bytes()));
            off += word.len();
        }
    }
    for word in witer {
        if off + word.len() + 1 > width {
            try!(buf.write_all(b"\n"));
            for _ in 0..indent {
                try!(buf.write_all(b" "));
            }
            off = indent;
        } else {
            try!(buf.write_all(b" "));
            off += 1;
        }
        try!(buf.write_all(word.as_bytes()));
        off += word.len();
    }
    return Ok(());
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashSet;
use std::ffi;
use std::fmt;
use std::mem;
use std::str;
use super::gl;

use gfx::device::Capabilities;
use gfx::device::shade;

/// A version number for a specific component of an OpenGL implementation
#[derive(Copy, Eq, PartialEq)]
pub struct Version {
    pub major: u32,
    pub minor: u32,
    pub revision: Option<u32>,
    pub vendor_info: &'static str,
}

// FIXME https://github.com/rust-lang/rust/issues/18738
// derive

#[automatically_derived]
impl ::std::cmp::Ord for Version {
    #[inline]
    fn cmp(&self, other: &Version) -> ::std::cmp::Ordering {
        (&self.major, &self.minor, &self.revision, self.vendor_info)
            .cmp(&(&other.major, &other.minor, &other.revision, other.vendor_info))
    }
}
#[automatically_derived]
impl ::std::cmp::PartialOrd for Version {
    #[inline]
    fn partial_cmp(&self, other: &Version) -> ::std::option::Option<::std::cmp::Ordering> {
        (&self.major, &self.minor, &self.revision, self.vendor_info)
            .partial_cmp(&(&other.major, &other.minor, &other.revision, other.vendor_info))
    }
}

impl Version {
    /// Create a new OpenGL version number
    pub fn new(major: u32, minor: u32, revision: Option<u32>,
               vendor_info: &'static str) -> Version {
        Version {
            major: major,
            minor: minor,
            revision: revision,
            vendor_info: vendor_info,
        }
    }

    /// According to the OpenGL specification, the version information is
    /// expected to follow the following syntax:
    ///
    /// ~~~bnf
    /// <major>       ::= <number>
    /// <minor>       ::= <number>
    /// <revision>    ::= <number>
    /// <vendor-info> ::= <string>
    /// <release>     ::= <major> "." <minor> ["." <release>]
    /// <version>     ::= <release> [" " <vendor-info>]
    /// ~~~
    ///
    /// Note that this function is intentionally lenient in regards to parsing,
    /// and will try to recover at least the first two version numbers without
    /// resulting in an `Err`.
    pub fn parse(src: &'static str) -> Result<Version, &'static str> {
        let (version, vendor_info) = match src.find(' ') {
            Some(i) => (&src[..i], &src[(i + 1)..]),
            None => (src, ""),
        };

        // TODO: make this even more lenient so that we can also accept
        // `<major> "." <minor> [<???>]`
        let mut it = version.split('.');
        let major = it.next().and_then(|s| s.parse().ok());
        let minor = it.next().and_then(|s| s.parse().ok());
        let revision = it.next().and_then(|s| s.parse().ok());

        match (major, minor, revision) {
            (Some(major), Some(minor), revision) => Ok(Version {
                major: major,
                minor: minor,
                revision: revision,
                vendor_info: vendor_info,
            }),
            (_, _, _) => Err(src),
        }
    }
}

impl fmt::Debug for Version {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match (self.major, self.minor, self.revision, self.vendor_info) {
            (major, minor, Some(revision), "") =>
                write!(f, "{}.{}.{}", major, minor, revision),
            (major, minor, None, "") =>
                write!(f, "{}.{}", major, minor),
            (major, minor, Some(revision), vendor_info) =>
                write!(f, "{}.{}.{}, {}", major, minor, revision, vendor_info),
            (major, minor, None, vendor_info) =>
                write!(f, "{}.{}, {}", major, minor, vendor_info),
        }
    }
}

/// Get a statically allocated string from the implementation using
/// `glGetString`. Fails if it `GLenum` cannot be handled by the
/// implementation's `gl.GetString` function.
fn get_string(gl: &gl::Gl, name: gl::types::GLenum) -> &'static str {
    let ptr = unsafe { gl.GetString(name) } as *const i8;
    if !ptr.is_null() {
        // This should be safe to mark as statically allocated because
        // GlGetString only returns static strings.
        unsafe { c_str_as_static_str(ptr) }
    } else {
        panic!("Invalid GLenum passed to `get_string`: {:x}", name)
    }
}

fn get_usize(gl: &gl::Gl, name: gl::types::GLenum) -> usize {
    let mut value = 0 as gl::types::GLint;
    unsafe { gl.GetIntegerv(name, &mut value) };
    value as usize
}

unsafe fn c_str_as_static_str(c_str: *const i8) -> &'static str {
    mem::transmute(str::from_utf8(ffi::CStr::from_ptr(c_str).to_bytes()).unwrap())
}

/// A unique platform identifier that does not change between releases
#[derive(Copy, Eq, PartialEq, Debug)]
pub struct PlatformName {
    /// The company responsible for the OpenGL implementation
    pub vendor: &'static str,
    /// The name of the renderer
    pub renderer: &'static str,
}

impl PlatformName {
    fn get(gl: &gl::Gl) -> PlatformName {
        PlatformName {
            vendor: get_string(gl, gl::VENDOR),
            renderer: get_string(gl, gl::RENDERER),
        }
    }
}

/// OpenGL implementation information
#[derive(Debug)]
pub struct Info {
    /// The platform identifier
    pub platform_name: PlatformName,
    /// The OpenGL API vesion number
    pub version: Version,
    /// The GLSL vesion number
    pub shading_language: Version,
    /// The extensions supported by the implementation
    pub extensions: HashSet<&'static str>,
}

impl Info {
    fn get(gl: &gl::Gl) -> Info {
        let platform_name = PlatformName::get(gl);
        let version = Version::parse(get_string(gl, gl::VERSION)).unwrap();
        let shading_language = Version::parse(get_string(gl, gl::SHADING_LANGUAGE_VERSION)).unwrap();
        let extensions = if version >= Version::new(3, 2, None, "") {
            let num_exts = get_usize(gl, gl::NUM_EXTENSIONS) as gl::types::GLuint;
            (0..num_exts)
                .map(|i| unsafe { c_str_as_static_str(gl.GetStringi(gl::EXTENSIONS, i) as *const i8) })
                .collect()
        } else {
            // Fallback
            get_string(gl, gl::EXTENSIONS).split(' ').collect()
        };
        Info {
            platform_name: platform_name,
            version: version,
            shading_language: shading_language,
            extensions: extensions,
        }
    }

    /// Returns `true` if the implementation supports the extension
    pub fn is_extension_supported(&self, s: &'static str) -> bool {
        self.extensions.contains(&s)
    }

    pub fn is_version_or_extension_supported(&self, major: u32, minor: u32, ext: &'static str) -> bool {
        self.version >= Version::new(major, minor, None, "") || self.is_extension_supported(ext)
    }
}

fn to_shader_model(v: &Version) -> shade::ShaderModel {
    use gfx::device::shade::ShaderModel;
    match v {
        v if *v < Version::new(1, 20, None, "") => ShaderModel::Unsupported,
        v if *v < Version::new(1, 50, None, "") => ShaderModel::Version30,
        v if *v < Version::new(3,  0, None, "") => ShaderModel::Version40,
        v if *v < Version::new(4, 30, None, "") => ShaderModel::Version41,
        _                                       => ShaderModel::Version50,
    }
}

/// Load the information pertaining to the driver and the corresponding device
/// capabilities.
pub fn get(gl: &gl::Gl) -> (Info, Capabilities) {
    let info = Info::get(gl);
    let caps = Capabilities {
        shader_model:                   to_shader_model(&info.shading_language),

        max_draw_buffers:               get_usize(gl, gl::MAX_DRAW_BUFFERS),
        max_texture_size:               get_usize(gl, gl::MAX_TEXTURE_SIZE),
        max_vertex_attributes:          get_usize(gl, gl::MAX_VERTEX_ATTRIBS),

        array_buffer_supported:         info.is_version_or_extension_supported(3, 0, "GL_ARB_vertex_array_object"),
        fragment_output_supported:      info.is_version_or_extension_supported(3, 0, "GL_ARB_gpu_shader4"),
        immutable_storage_supported:    info.is_version_or_extension_supported(4, 2, "GL_ARB_texture_storage"),
        instance_base_supported:        info.is_version_or_extension_supported(4, 2, "GL_ARB_base_instance"),
        instance_call_supported:        info.is_version_or_extension_supported(3, 1, "GL_ARB_draw_instanced"),
        instance_rate_supported:        info.is_version_or_extension_supported(3, 3, "GL_ARB_instanced_arrays"),
        render_targets_supported:       info.is_version_or_extension_supported(3, 0, "GL_ARB_framebuffer_object"),
        sampler_objects_supported:      info.is_version_or_extension_supported(3, 3, "GL_ARB_sampler_objects"),
        uniform_block_supported:        info.is_version_or_extension_supported(3, 0, "GL_ARB_uniform_buffer_object"),
        vertex_base_supported:          info.is_version_or_extension_supported(3, 2, "GL_ARB_draw_elements_base_vertex"),
    };
    (info, caps)
}

#[cfg(test)]
mod tests {
    use super::Version;
    use super::to_shader_model;

    #[test]
    fn test_version_parse() {
        assert_eq!(Version::parse("1"), Err("1"));
        assert_eq!(Version::parse("1."), Err("1."));
        assert_eq!(Version::parse("1 h3l1o. W0rld"), Err("1 h3l1o. W0rld"));
        assert_eq!(Version::parse("1. h3l1o. W0rld"), Err("1. h3l1o. W0rld"));
        assert_eq!(Version::parse("1.2.3"), Ok(Version::new(1, 2, Some(3), "")));
        assert_eq!(Version::parse("1.2"), Ok(Version::new(1, 2, None, "")));
        assert_eq!(Version::parse("1.2 h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.h3l1o. W0rld"), Ok(Version::new(1, 2, None, "W0rld")));
        assert_eq!(Version::parse("1.2. h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.3.h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "W0rld")));
        assert_eq!(Version::parse("1.2.3 h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "h3l1o. W0rld")));
    }

    #[test]
    fn test_shader_model() {
        assert_eq!(to_shader_model(&Version::parse("1.10").unwrap()), ShaderModel::Unsupported);
        assert_eq!(to_shader_model(&Version::parse("1.20").unwrap()), ShaderModel::Version30);
        assert_eq!(to_shader_model(&Version::parse("1.50").unwrap()), ShaderModel::Version40);
        assert_eq!(to_shader_model(&Version::parse("3.00").unwrap()), ShaderModel::Version41);
        assert_eq!(to_shader_model(&Version::parse("4.30").unwrap()), ShaderModel::Version50);
    }
}
<commit_msg>Fixed shader version tests<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashSet;
use std::ffi;
use std::fmt;
use std::mem;
use std::str;
use super::gl;

use gfx::device::Capabilities;
use gfx::device::shade;

/// A version number for a specific component of an OpenGL implementation
#[derive(Copy, Eq, PartialEq)]
pub struct Version {
    pub major: u32,
    pub minor: u32,
    pub revision: Option<u32>,
    pub vendor_info: &'static str,
}

// FIXME https://github.com/rust-lang/rust/issues/18738
// derive

#[automatically_derived]
impl ::std::cmp::Ord for Version {
    #[inline]
    fn cmp(&self, other: &Version) -> ::std::cmp::Ordering {
        (&self.major, &self.minor, &self.revision, self.vendor_info)
            .cmp(&(&other.major, &other.minor, &other.revision, other.vendor_info))
    }
}
#[automatically_derived]
impl ::std::cmp::PartialOrd for Version {
    #[inline]
    fn partial_cmp(&self, other: &Version) -> ::std::option::Option<::std::cmp::Ordering> {
        (&self.major, &self.minor, &self.revision, self.vendor_info)
            .partial_cmp(&(&other.major, &other.minor, &other.revision, other.vendor_info))
    }
}

impl Version {
    /// Create a new OpenGL version number
    pub fn new(major: u32, minor: u32, revision: Option<u32>,
               vendor_info: &'static str) -> Version {
        Version {
            major: major,
            minor: minor,
            revision: revision,
            vendor_info: vendor_info,
        }
    }

    /// According to the OpenGL specification, the version information is
    /// expected to follow the following syntax:
    ///
    /// ~~~bnf
    /// <major>       ::= <number>
    /// <minor>       ::= <number>
    /// <revision>    ::= <number>
    /// <vendor-info> ::= <string>
    /// <release>     ::= <major> "." <minor> ["." <release>]
    /// <version>     ::= <release> [" " <vendor-info>]
    /// ~~~
    ///
    /// Note that this function is intentionally lenient in regards to parsing,
    /// and will try to recover at least the first two version numbers without
    /// resulting in an `Err`.
    pub fn parse(src: &'static str) -> Result<Version, &'static str> {
        let (version, vendor_info) = match src.find(' ') {
            Some(i) => (&src[..i], &src[(i + 1)..]),
            None => (src, ""),
        };

        // TODO: make this even more lenient so that we can also accept
        // `<major> "." <minor> [<???>]`
        let mut it = version.split('.');
        let major = it.next().and_then(|s| s.parse().ok());
        let minor = it.next().and_then(|s| s.parse().ok());
        let revision = it.next().and_then(|s| s.parse().ok());

        match (major, minor, revision) {
            (Some(major), Some(minor), revision) => Ok(Version {
                major: major,
                minor: minor,
                revision: revision,
                vendor_info: vendor_info,
            }),
            (_, _, _) => Err(src),
        }
    }
}

impl fmt::Debug for Version {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match (self.major, self.minor, self.revision, self.vendor_info) {
            (major, minor, Some(revision), "") =>
                write!(f, "{}.{}.{}", major, minor, revision),
            (major, minor, None, "") =>
                write!(f, "{}.{}", major, minor),
            (major, minor, Some(revision), vendor_info) =>
                write!(f, "{}.{}.{}, {}", major, minor, revision, vendor_info),
            (major, minor, None, vendor_info) =>
                write!(f, "{}.{}, {}", major, minor, vendor_info),
        }
    }
}

/// Get a statically allocated string from the implementation using
/// `glGetString`. Fails if it `GLenum` cannot be handled by the
/// implementation's `gl.GetString` function.
fn get_string(gl: &gl::Gl, name: gl::types::GLenum) -> &'static str {
    let ptr = unsafe { gl.GetString(name) } as *const i8;
    if !ptr.is_null() {
        // This should be safe to mark as statically allocated because
        // GlGetString only returns static strings.
        unsafe { c_str_as_static_str(ptr) }
    } else {
        panic!("Invalid GLenum passed to `get_string`: {:x}", name)
    }
}

fn get_usize(gl: &gl::Gl, name: gl::types::GLenum) -> usize {
    let mut value = 0 as gl::types::GLint;
    unsafe { gl.GetIntegerv(name, &mut value) };
    value as usize
}

unsafe fn c_str_as_static_str(c_str: *const i8) -> &'static str {
    mem::transmute(str::from_utf8(ffi::CStr::from_ptr(c_str).to_bytes()).unwrap())
}

/// A unique platform identifier that does not change between releases
#[derive(Copy, Eq, PartialEq, Debug)]
pub struct PlatformName {
    /// The company responsible for the OpenGL implementation
    pub vendor: &'static str,
    /// The name of the renderer
    pub renderer: &'static str,
}

impl PlatformName {
    fn get(gl: &gl::Gl) -> PlatformName {
        PlatformName {
            vendor: get_string(gl, gl::VENDOR),
            renderer: get_string(gl, gl::RENDERER),
        }
    }
}

/// OpenGL implementation information
#[derive(Debug)]
pub struct Info {
    /// The platform identifier
    pub platform_name: PlatformName,
    /// The OpenGL API vesion number
    pub version: Version,
    /// The GLSL vesion number
    pub shading_language: Version,
    /// The extensions supported by the implementation
    pub extensions: HashSet<&'static str>,
}

impl Info {
    fn get(gl: &gl::Gl) -> Info {
        let platform_name = PlatformName::get(gl);
        let version = Version::parse(get_string(gl, gl::VERSION)).unwrap();
        let shading_language = Version::parse(get_string(gl, gl::SHADING_LANGUAGE_VERSION)).unwrap();
        let extensions = if version >= Version::new(3, 2, None, "") {
            let num_exts = get_usize(gl, gl::NUM_EXTENSIONS) as gl::types::GLuint;
            (0..num_exts)
                .map(|i| unsafe { c_str_as_static_str(gl.GetStringi(gl::EXTENSIONS, i) as *const i8) })
                .collect()
        } else {
            // Fallback
            get_string(gl, gl::EXTENSIONS).split(' ').collect()
        };
        Info {
            platform_name: platform_name,
            version: version,
            shading_language: shading_language,
            extensions: extensions,
        }
    }

    /// Returns `true` if the implementation supports the extension
    pub fn is_extension_supported(&self, s: &'static str) -> bool {
        self.extensions.contains(&s)
    }

    pub fn is_version_or_extension_supported(&self, major: u32, minor: u32, ext: &'static str) -> bool {
        self.version >= Version::new(major, minor, None, "") || self.is_extension_supported(ext)
    }
}

fn to_shader_model(v: &Version) -> shade::ShaderModel {
    use gfx::device::shade::ShaderModel;
    match v {
        v if *v < Version::new(1, 20, None, "") => ShaderModel::Unsupported,
        v if *v < Version::new(1, 50, None, "") => ShaderModel::Version30,
        v if *v < Version::new(3,  0, None, "") => ShaderModel::Version40,
        v if *v < Version::new(4, 30, None, "") => ShaderModel::Version41,
        _                                       => ShaderModel::Version50,
    }
}

/// Load the information pertaining to the driver and the corresponding device
/// capabilities.
pub fn get(gl: &gl::Gl) -> (Info, Capabilities) {
    let info = Info::get(gl);
    let caps = Capabilities {
        shader_model:                   to_shader_model(&info.shading_language),

        max_draw_buffers:               get_usize(gl, gl::MAX_DRAW_BUFFERS),
        max_texture_size:               get_usize(gl, gl::MAX_TEXTURE_SIZE),
        max_vertex_attributes:          get_usize(gl, gl::MAX_VERTEX_ATTRIBS),

        array_buffer_supported:         info.is_version_or_extension_supported(3, 0, "GL_ARB_vertex_array_object"),
        fragment_output_supported:      info.is_version_or_extension_supported(3, 0, "GL_ARB_gpu_shader4"),
        immutable_storage_supported:    info.is_version_or_extension_supported(4, 2, "GL_ARB_texture_storage"),
        instance_base_supported:        info.is_version_or_extension_supported(4, 2, "GL_ARB_base_instance"),
        instance_call_supported:        info.is_version_or_extension_supported(3, 1, "GL_ARB_draw_instanced"),
        instance_rate_supported:        info.is_version_or_extension_supported(3, 3, "GL_ARB_instanced_arrays"),
        render_targets_supported:       info.is_version_or_extension_supported(3, 0, "GL_ARB_framebuffer_object"),
        sampler_objects_supported:      info.is_version_or_extension_supported(3, 3, "GL_ARB_sampler_objects"),
        uniform_block_supported:        info.is_version_or_extension_supported(3, 0, "GL_ARB_uniform_buffer_object"),
        vertex_base_supported:          info.is_version_or_extension_supported(3, 2, "GL_ARB_draw_elements_base_vertex"),
    };
    (info, caps)
}

#[cfg(test)]
mod tests {
    use super::Version;
    use super::to_shader_model;

    #[test]
    fn test_version_parse() {
        assert_eq!(Version::parse("1"), Err("1"));
        assert_eq!(Version::parse("1."), Err("1."));
        assert_eq!(Version::parse("1 h3l1o. W0rld"), Err("1 h3l1o. W0rld"));
        assert_eq!(Version::parse("1. h3l1o. W0rld"), Err("1. h3l1o. W0rld"));
        assert_eq!(Version::parse("1.2.3"), Ok(Version::new(1, 2, Some(3), "")));
        assert_eq!(Version::parse("1.2"), Ok(Version::new(1, 2, None, "")));
        assert_eq!(Version::parse("1.2 h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.h3l1o. W0rld"), Ok(Version::new(1, 2, None, "W0rld")));
        assert_eq!(Version::parse("1.2. h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.3.h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "W0rld")));
        assert_eq!(Version::parse("1.2.3 h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "h3l1o. W0rld")));
    }

    #[test]
    fn test_shader_model() {
        use gfx::device::shade::ShaderModel;
        assert_eq!(to_shader_model(&Version::parse("1.10").unwrap()), ShaderModel::Unsupported);
        assert_eq!(to_shader_model(&Version::parse("1.20").unwrap()), ShaderModel::Version30);
        assert_eq!(to_shader_model(&Version::parse("1.50").unwrap()), ShaderModel::Version40);
        assert_eq!(to_shader_model(&Version::parse("3.00").unwrap()), ShaderModel::Version41);
        assert_eq!(to_shader_model(&Version::parse("4.30").unwrap()), ShaderModel::Version50);
    }
}
<|endoftext|>


<commit_before>use proc_macro::{token_stream, TokenStream, TokenTree};
use std::iter::Peekable;

pub type Iter<'a> = &'a mut IterImpl;

pub struct IterImpl {
    tokens: Peekable<token_stream::IntoIter>,
}

pub fn new(tokens: TokenStream) -> IterImpl {
    IterImpl {
        tokens: tokens.into_iter().peekable(),
    }
}

impl IterImpl {
    pub fn peek(&mut self) -> Option<&TokenTree> {
        self.tokens.peek()
    }
}

impl Iterator for IterImpl {
    type Item = TokenTree;

    fn next(&mut self) -> Option<Self::Item> {
        self.tokens.next()
    }
}
<commit_msg>Traverse into None-delimited groups<commit_after>use proc_macro::{token_stream, Delimiter, TokenStream, TokenTree};

pub type Iter<'a> = &'a mut IterImpl;

pub struct IterImpl {
    stack: Vec<token_stream::IntoIter>,
    peeked: Option<TokenTree>,
}

pub fn new(tokens: TokenStream) -> IterImpl {
    IterImpl {
        stack: vec![tokens.into_iter()],
        peeked: None,
    }
}

impl IterImpl {
    pub fn peek(&mut self) -> Option<&TokenTree> {
        self.peeked = self.next();
        self.peeked.as_ref()
    }
}

impl Iterator for IterImpl {
    type Item = TokenTree;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(tt) = self.peeked.take() {
            return Some(tt);
        }
        loop {
            let top = self.stack.last_mut()?;
            match top.next() {
                None => drop(self.stack.pop()),
                Some(TokenTree::Group(ref group)) if group.delimiter() == Delimiter::None => {
                    self.stack.push(group.stream().into_iter());
                }
                Some(tt) => return Some(tt),
            }
        }
    }
}
<|endoftext|>