<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::{LayoutOf, Size};
use rustc::ty::{self, TyCtxt};

use crate::*;

/// 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>,
}

/// Details of premature program termination.
pub enum TerminationInfo {
    Exit(i64),
    Abort,
}

/// Returns a freshly created `InterpCx`, along with an `MPlaceTy` representing
/// the location where the return value of the `start` lang item will be
/// written to.
/// Public because this is also 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>>, MPlaceTy<'tcx, Tag>)> {
    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();

    // First argument: pointer to `main()`.
    let main_ptr = ecx
        .memory
        .create_fn_alloc(FnVal::Instance(main_instance));
    // Second argument (argc): length of `config.args`.
    let argc = Scalar::from_uint(config.args.len() as u128, ecx.pointer_size());
    // Third argument (`argv`): created from `config.args`.
    let argv = {
        // For Windows, construct a command string with all the aguments (before we take apart `config.args`).
        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(arg, place.into())?;
        }
        ecx.memory
            .mark_immutable(argvs_place.ptr.assert_ptr().alloc_id)?;
        // A pointer to that place is the argument.
        let argv = argvs_place.ptr;
        // Store `argc` and `argv` for macOS `_NSGetArg{c,v}`.
        {
            let argc_place = ecx.allocate(
                ecx.layout_of(tcx.types.isize)?,
                MiriMemoryKind::Env.into(),
            );
            ecx.write_scalar(argc, argc_place.into())?;
            ecx.machine.argc = Some(argc_place.ptr);

            let argv_place = ecx.allocate(
                ecx.layout_of(tcx.mk_imm_ptr(tcx.types.unit))?,
                MiriMemoryKind::Env.into(),
            );
            ecx.write_scalar(argv, argv_place.into())?;
            ecx.machine.argv = Some(argv_place.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);
            // 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())?;
            }
        }
        argv
    };

    // Return place (in static memory so that it does not count as leak).
    let ret_place = ecx.allocate(
        ecx.layout_of(tcx.types.isize)?,
        MiriMemoryKind::Static.into(),
    );
    // Call start function.
    ecx.call_function(
        start_instance,
        &[main_ptr.into(), argc.into(), argv.into()],
        Some(ret_place.into()),
        StackPopCleanup::None { cleanup: true },
    )?;

    // 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, ret_place))
}

/// Evaluates the main function specified by `main_id`.
/// Returns `Some(return_code)` if program executed completed.
/// Returns `None` if an evaluation error occured.
pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) -> Option<i64> {
    let (mut ecx, ret_place) = match create_ecx(tcx, main_id, config) {
        Ok(v) => v,
        Err(mut err) => {
            err.print_backtrace();
            panic!("Miri initialziation error: {}", err.kind)
        }
    };

    // Perform the main execution.
    let res: InterpResult<'_, i64> = (|| {
        ecx.run()?;
        // Read the return code pointer *before* we run TLS destructors, to assert
        // that it was written to by the time that `start` lang item returned.
        let return_code = ecx.read_scalar(ret_place.into())?.not_undef()?.to_machine_isize(&ecx)?;
        ecx.run_tls_dtors()?;
        Ok(return_code)
    })();

    // Process the result.
    match res {
        Ok(return_code) => {
            // 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 {
                let leaks = ecx.memory.leak_report();
                if leaks != 0 {
                    tcx.sess.err("the evaluated program leaked memory");
                    // Ignore the provided return code - let the reported error
                    // determine the return code.
                    return None;
                }
            }
            return Some(return_code)
        }
        Err(mut e) => {
            // Special treatment for some error kinds
            let msg = match e.kind {
                InterpError::MachineStop(ref info) => {
                    let info = info.downcast_ref::<TerminationInfo>()
                        .expect("invalid MachineStop payload");
                    match info {
                        TerminationInfo::Exit(code) => return Some(*code),
                        TerminationInfo::Abort =>
                            format!("the evaluated program aborted execution")
                    }
                }
                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 span = frame.current_source_info().unwrap().span;

                let msg = format!("Miri evaluation error: {}", msg);
                let mut err = ecx.tcx.sess.struct_span_err(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);
                }
            }
            // Let the reported error determine the return code.
            return None;
        }
    }
}
<commit_msg>some error classes should be impossible<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 crate::*;

/// 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>,
}

/// Details of premature program termination.
pub enum TerminationInfo {
    Exit(i64),
    Abort,
}

/// Returns a freshly created `InterpCx`, along with an `MPlaceTy` representing
/// the location where the return value of the `start` lang item will be
/// written to.
/// Public because this is also 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>>, MPlaceTy<'tcx, Tag>)> {
    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();

    // First argument: pointer to `main()`.
    let main_ptr = ecx
        .memory
        .create_fn_alloc(FnVal::Instance(main_instance));
    // Second argument (argc): length of `config.args`.
    let argc = Scalar::from_uint(config.args.len() as u128, ecx.pointer_size());
    // Third argument (`argv`): created from `config.args`.
    let argv = {
        // For Windows, construct a command string with all the aguments (before we take apart `config.args`).
        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(arg, place.into())?;
        }
        ecx.memory
            .mark_immutable(argvs_place.ptr.assert_ptr().alloc_id)?;
        // A pointer to that place is the argument.
        let argv = argvs_place.ptr;
        // Store `argc` and `argv` for macOS `_NSGetArg{c,v}`.
        {
            let argc_place = ecx.allocate(
                ecx.layout_of(tcx.types.isize)?,
                MiriMemoryKind::Env.into(),
            );
            ecx.write_scalar(argc, argc_place.into())?;
            ecx.machine.argc = Some(argc_place.ptr);

            let argv_place = ecx.allocate(
                ecx.layout_of(tcx.mk_imm_ptr(tcx.types.unit))?,
                MiriMemoryKind::Env.into(),
            );
            ecx.write_scalar(argv, argv_place.into())?;
            ecx.machine.argv = Some(argv_place.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);
            // 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())?;
            }
        }
        argv
    };

    // Return place (in static memory so that it does not count as leak).
    let ret_place = ecx.allocate(
        ecx.layout_of(tcx.types.isize)?,
        MiriMemoryKind::Static.into(),
    );
    // Call start function.
    ecx.call_function(
        start_instance,
        &[main_ptr.into(), argc.into(), argv.into()],
        Some(ret_place.into()),
        StackPopCleanup::None { cleanup: true },
    )?;

    // 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, ret_place))
}

/// Evaluates the main function specified by `main_id`.
/// Returns `Some(return_code)` if program executed completed.
/// Returns `None` if an evaluation error occured.
pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) -> Option<i64> {
    let (mut ecx, ret_place) = match create_ecx(tcx, main_id, config) {
        Ok(v) => v,
        Err(mut err) => {
            err.print_backtrace();
            panic!("Miri initialziation error: {}", err.kind)
        }
    };

    // Perform the main execution.
    let res: InterpResult<'_, i64> = (|| {
        ecx.run()?;
        // Read the return code pointer *before* we run TLS destructors, to assert
        // that it was written to by the time that `start` lang item returned.
        let return_code = ecx.read_scalar(ret_place.into())?.not_undef()?.to_machine_isize(&ecx)?;
        ecx.run_tls_dtors()?;
        Ok(return_code)
    })();

    // Process the result.
    match res {
        Ok(return_code) => {
            // 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 {
                let leaks = ecx.memory.leak_report();
                if leaks != 0 {
                    tcx.sess.err("the evaluated program leaked memory");
                    // Ignore the provided return code - let the reported error
                    // determine the return code.
                    return None;
                }
            }
            return Some(return_code)
        }
        Err(mut e) => {
            // Special treatment for some error kinds
            let msg = match e.kind {
                InterpError::MachineStop(ref info) => {
                    let info = info.downcast_ref::<TerminationInfo>()
                        .expect("invalid MachineStop payload");
                    match info {
                        TerminationInfo::Exit(code) => return Some(*code),
                        TerminationInfo::Abort =>
                            format!("the evaluated program aborted execution")
                    }
                }
                err_unsup!(NoMirFor(..)) =>
                    format!("{}. Did you set `MIRI_SYSROOT` to a Miri-enabled sysroot? You can prepare one with `cargo miri setup`.", e),
                InterpError::Panic(_) | InterpError::InvalidProgram(_) =>
                    bug!("This error should be impossible in Miri: {}", e),
                _ => e.to_string()
            };
            e.print_backtrace();
            if let Some(frame) = ecx.stack().last() {
                let span = frame.current_source_info().unwrap().span;

                let msg = format!("Miri evaluation error: {}", msg);
                let mut err = ecx.tcx.sess.struct_span_err(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);
                }
            }
            // Let the reported error determine the return code.
            return None;
        }
    }
}
<|endoftext|>


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

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

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

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

/// Configuration needed to spawn a Miri instance.
#[derive(Clone)]
pub struct MiriConfig {
    pub validate: bool,
    pub args: Vec<String>,

    // The seed to use when non-determinism is required (e.g. 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>>> {

    // FIXME(https://github.com/rust-lang/miri/pull/803): no validation on Windows.
    let target_os = tcx.sess.target.target.target_os.to_lowercase();
    let validate = if target_os == "windows" {
        false
    } else {
        config.validate
    };

    let mut ecx = InterpCx::new(
        tcx.at(syntax::source_map::DUMMY_SP),
        ty::ParamEnv::reveal_all(),
        Evaluator::new(),
        MemoryExtra::new(config.seed.map(StdRng::seed_from_u64), validate),
    );

    let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);
    let main_mir = ecx.load_mir(main_instance.def)?;

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

    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::Kind::from(main_ret_ty)))
        ).unwrap();
    let start_mir = ecx.load_mir(start_instance.def)?;

    if start_mir.arg_count != 3 {
        return err!(AbiViolation(format!(
            "'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_mut().create_fn_alloc(FnVal::Instance(main_instance));
    let dest = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(args.next().unwrap())))?;
    ecx.write_scalar(Scalar::Ptr(main_ptr), dest)?;

    // Second argument (argc): `1`.
    let dest = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(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()?);
    }

    // FIXME: extract main source file path.
    // Third argument (`argv`): created from `config.args`.
    let dest = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(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_mut().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_mut().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 tcx = &{ecx.tcx.tcx};
        let cmd_utf16: Vec<u16> = cmd.encode_utf16().collect();
        let cmd_ptr = ecx.memory_mut().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.
        let char_size = Size::from_bytes(2);
        let cmd_alloc = ecx.memory_mut().get_mut(cmd_ptr.alloc_id)?;
        let mut cur_ptr = cmd_ptr;
        for &c in cmd_utf16.iter() {
            cmd_alloc.write_scalar(
                tcx,
                cur_ptr,
                Scalar::from_uint(c, char_size).into(),
                char_size,
            )?;
            cur_ptr = cur_ptr.offset(char_size, tcx)?;
        }
    }

    assert!(args.next().is_none(), "start lang item has more arguments than expected");

    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),
                InterpError::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>we do this these days<commit_after>//! Main evaluator loop and setting up the initial stack frame.

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

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

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

/// Configuration needed to spawn a Miri instance.
#[derive(Clone)]
pub struct MiriConfig {
    pub validate: bool,
    pub args: Vec<String>,

    // The seed to use when non-determinism is required (e.g. 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>>> {

    // FIXME(https://github.com/rust-lang/miri/pull/803): no validation on Windows.
    let target_os = tcx.sess.target.target.target_os.to_lowercase();
    let validate = if target_os == "windows" {
        false
    } else {
        config.validate
    };

    let mut ecx = InterpCx::new(
        tcx.at(syntax::source_map::DUMMY_SP),
        ty::ParamEnv::reveal_all(),
        Evaluator::new(),
        MemoryExtra::new(config.seed.map(StdRng::seed_from_u64), validate),
    );

    let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);
    let main_mir = ecx.load_mir(main_instance.def)?;

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

    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::Kind::from(main_ret_ty)))
        ).unwrap();
    let start_mir = ecx.load_mir(start_instance.def)?;

    if start_mir.arg_count != 3 {
        return err!(AbiViolation(format!(
            "'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_mut().create_fn_alloc(FnVal::Instance(main_instance));
    let dest = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(args.next().unwrap())))?;
    ecx.write_scalar(Scalar::Ptr(main_ptr), dest)?;

    // Second argument (argc): `1`.
    let dest = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(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.eval_place(&mir::Place::Base(mir::PlaceBase::Local(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_mut().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_mut().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 tcx = &{ecx.tcx.tcx};
        let cmd_utf16: Vec<u16> = cmd.encode_utf16().collect();
        let cmd_ptr = ecx.memory_mut().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.
        let char_size = Size::from_bytes(2);
        let cmd_alloc = ecx.memory_mut().get_mut(cmd_ptr.alloc_id)?;
        let mut cur_ptr = cmd_ptr;
        for &c in cmd_utf16.iter() {
            cmd_alloc.write_scalar(
                tcx,
                cur_ptr,
                Scalar::from_uint(c, char_size).into(),
                char_size,
            )?;
            cur_ptr = cur_ptr.offset(char_size, tcx)?;
        }
    }

    assert!(args.next().is_none(), "start lang item has more arguments than expected");

    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),
                InterpError::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 std::cell::RefCell;
use std::rc::Rc;

use ast::*;
use env::*;
use parser::*;

pub struct Evaler {
    parser: Parser,
    env:    Rc<RefCell<Env>>
}

#[allow(dead_code)]
impl Evaler {
    pub fn new() -> Evaler {
        let mut env = Env::new();
        Evaler {
            parser: Parser::new(),
            env:    Rc::new(RefCell::new(env))
        }
    }

    pub fn eval(&mut self, code: String) -> Option<ExprAst> {
        self.parser.load(code);
        let mut res = None;
        loop {
            let exp = self.parser.read_exp();
            match exp {
                Some(_exp) => {
                    let r = self.eval_exp(_exp);
                    r.print();
                    res = Some(r);
                },
                None => break
            }
        }
        res
    }

    fn eval_exp(&mut self, exp: ExprAst) -> ExprAst {
        if exp.is_self()   { return exp; }
        if exp.is_symbol() { return self.env.clone().borrow().lookup(exp).unwrap(); }
        if exp.is_quote()  { return exp.cdr().car(); }
        if exp.is_assign() { return self.eval_assign(exp); }
        if exp.is_def()    { return self.eval_def(exp); }
        if exp.is_begin()  { return self.eval_begin(exp); }
        if exp.is_if()     { return self.eval_if(exp); }
        if exp.is_lambda() { return self.eval_lambda(exp); }
        if exp.is_and()    { return self.eval_and(exp); }
        if exp.is_or()     { return self.eval_or(exp); }
        if exp.is_cond()   { return self.eval_cond(exp); }
        if exp.is_let()    { return self.eval_let(exp); }
        if exp.is_pair()   { return self.eval_app(exp); }
        ExprAst::Symbol(SymbolNode::new("OK"))
    }

    fn eval_assign(&mut self, exp: ExprAst) -> ExprAst {
        let var = exp.c("da");
        let val = self.eval_exp(exp.c("dda"));
        self.env.clone().borrow_mut().def_var(var, val);
        ExprAst::Symbol(SymbolNode::new("OK"))
    }

    fn eval_def(&mut self, exp: ExprAst) -> ExprAst {
        let var = exp.def_var();
        let val = exp.def_val();
        let val = self.eval_exp(val);
        self.env.clone().borrow_mut().def_var(var, val);
        ExprAst::Symbol(SymbolNode::new("OK"))
    }

    fn eval_if(&mut self, exp: ExprAst) -> ExprAst {
        let pred = exp.c("da");
        let blk_t = exp.c("dda");
        let blk_f = exp.c("ddd");
        let res = self.eval_exp(pred);
        if res.is_true() {
            self.eval_exp(blk_t)
        } else {
            if res.is_empty() {
                ExprAst::Bool(BoolNode::new(false))
            } else {
                self.eval_exp(blk_f.car())
            }
        }
    }

    fn eval_and(&mut self, exp: ExprAst) -> ExprAst {
        let mut elems = exp.cdr();
        if elems.is_empty() {
            return ExprAst::Bool(BoolNode::new(true));
        }
        loop {
            if elems.is_last() { break; }
            let res = self.eval_exp(elems.car());
            if res.is_false() {
                return ExprAst::Bool(BoolNode::new(false));
            }
            elems = elems.cdr();
        }
        self.eval_exp(elems.car())
    }

    fn eval_or(&mut self, exp: ExprAst) -> ExprAst {
        let mut elems = exp.cdr();
        if elems.is_empty() {
            return ExprAst::Bool(BoolNode::new(true));
        }
        loop {
            if elems.is_last() { break; }
            let res = self.eval_exp(elems.car());
            if res.is_true() {
                return ExprAst::Bool(BoolNode::new(true));
            }
            elems = elems.cdr();
        }
        self.eval_exp(elems.car())
    }

    fn eval_cond(&mut self, exp: ExprAst) -> ExprAst {
        let mut elems = exp.cdr().car();
        loop {
            if elems.is_empty() { break; }
            let cur = elems.car();
            let val = self.eval_exp(cur.clone());
            if val.is_true() || val == ExprAst::Symbol(SymbolNode::new("else")) {
                return self.eval_exp(cur.clone().cdr().car());
            }
            elems = elems.cdr();
        }
        ExprAst::Bool(BoolNode::new(true))
    }

    fn eval_let(&mut self, exp: ExprAst) -> ExprAst {
        fn bind_params(exp: ExprAst) -> ExprAst {
            if exp.is_empty() {
                ExprAst::Nil
            } else {
                ExprAst::Pair(PairNode::new(exp.c("aa"), bind_params(exp.cdr())))
            }
        }

        fn bind_args(exp: ExprAst) -> ExprAst {
            if exp.is_empty() {
                ExprAst::Nil
            } else {
                ExprAst::Pair(PairNode::new(exp.c("ada"), bind_args(exp.cdr())))
            }
        }

        let bindings = exp.c("da");
        bindings.print();
        let obj = ExprAst::Pair(PairNode::new(
            bind_params(bindings.clone()).make_lambda(exp.c("dd")),
            bind_args(bindings)));
        self.eval_exp(obj)
    }

    fn eval_begin(&mut self, exp: ExprAst) -> ExprAst {
        let mut _exp = exp.cdr();
        loop {
            if _exp.is_last() { break; }
            self.eval_exp(_exp.car());
            _exp = _exp.cdr();
        }
        self.eval_exp(_exp.car())
    }

    fn eval_values(&mut self, exprs: ExprAst) -> ExprAst {
        if exprs.is_empty() {
            ExprAst::Nil
        } else {
            let first = self.eval_exp(exprs.car());
            ExprAst::Pair(PairNode::new(first, self.eval_values(exprs.cdr())))
        }
    }

    fn eval_app(&mut self, expr: ExprAst) -> ExprAst {
        let _proc = self.eval_exp(expr.car());
        let _args = self.eval_values(expr.cdr());
        if _proc.is_proc() {
            let func = _proc.as_proc().func();
            return func(_args);
        } else {
            assert!(_proc.is_cproc());
            let _vars = _proc.params();
            self.env = self.env.clone().borrow_mut().extend(_vars, _args);
            let begin = ExprAst::Symbol(SymbolNode::new("begin"));
            let res = self.eval_exp(ExprAst::Pair(PairNode::new(begin, _proc.body())));
            self.env = self.env.clone().borrow_mut().parent().unwrap();
            res
        }
    }

    fn eval_lambda(&mut self, expr: ExprAst) -> ExprAst {
        ExprAst::CompProc(CompProcNode::new(expr.c("da"),  //vars
                                            expr.c("dd"),  //body
                                            self.env.clone()))
    }
}

#[test]
fn test_evaler() {
    macro_rules! test_case {
        ($test_str:expr, $expect_type:ident, $expect_val:expr) => { {
            let mut evaler = Evaler::new();
            let res = evaler.eval($test_str.to_string()).unwrap();
            if res.$expect_type() != $expect_val {
                assert!(false);
            }
        }}
    }
    test_case!("11", as_int, 11);
    test_case!("'a", as_str, "a");
    test_case!(r#""hello""#, as_str, "hello");
    test_case!("#t", as_bool, true);
    test_case!("(and #t #t)", as_bool, true);
    test_case!("(and #t #f)", as_bool, false);
    test_case!("(or #t #t)", as_bool, true);
    test_case!("(or #f #f)", as_bool, false);
    test_case!("(or #t #f)", as_bool, true);
    test_case!("(set! a 1)", as_str, "OK");
    test_case!("(integer? 1)", as_bool, true);
    test_case!("(boolean? #t)", as_bool, true);
    test_case!("(boolean? #f)", as_bool, true);
    test_case!("(integer? #t)", as_bool, false);
    test_case!("(+ 1 1)", as_int, 2);
    test_case!("(- 1 1)", as_int, 0);
    test_case!("(+ 1 1  1)", as_int, 3);
    test_case!("(+ 1 1 -1 -1)", as_int, 0);
    test_case!("(/ 2 1)", as_int, 2);
    test_case!("(* 2 2)", as_int, 4);
    test_case!("(> 2 1)", as_bool, true);
    test_case!("(> 1 2)", as_bool, false);
    test_case!("(> 1 1)", as_bool, false);
    test_case!("(- (+ 3 (* 8 5)) 1)", as_int, 42);
    test_case!("(eq? 1 1)", as_bool, true);
    test_case!("(eq? 1 2)", as_bool, false);
    test_case!("(eq? 1 #f)", as_bool, false);
    test_case!("(pair? '())", as_bool, true);
    test_case!("(> (+ 1 1) 0)", as_bool, true);
    test_case!("(if (> 1 0) 1 else 2)", as_int, 1);
    test_case!("(begin (set! a 1) a)", as_int, 1);
    test_case!("(let ((a 1)) (+ a 1))", as_int, 2);
    test_case!("(let ((a 1)) (> (+ a 1) 0))", as_bool, true);
    test_case!("(let ((a 1) (b 2)) (> a b))", as_bool, false);
    test_case!("(define (add a b) (+ a b))", as_str, "OK");
    test_case!("(cond ((eq? 1 1) 1) ((> 1 2) 2) ))", as_int, 1);
    test_case!("(begin (set! x 5) (set! x 4) (+ x 1))", as_int, 5);
    test_case!("(car '(1 2))", as_int, 1);
    test_case!("(car (cdr '(1 2)))", as_int, 2);
    test_case!("(car (cons 1 2))", as_int, 1);
    test_case!("(car (cons (pair? '(1)) pair? '()))", as_bool, true);
    test_case!("(car (cdr (cons 1 2)))", as_int, 2);
    test_case!("(car (car (cons (cons 2 3) (cons 1 2))))", as_int, 2);
    test_case!("((lambda (x) x) 1)", as_int, 1);
    test_case!("((lambda (x y) (+ x y )) 1 2)", as_int, 3);
    test_case!("(define add4 (let ((x 4)) (lambda (y) (+ x y))))", as_str, "OK");
    test_case!("(begin 1 2)", as_int, 2);
    test_case!("((lambda (x) x) 5)", as_int, 5);
    test_case!("(let ((fu (lambda (x) (+ x 1)))) (fu 1))", as_int, 2);
    test_case!("((lambda ( x y ) ( if ( = y 0) 1 (* y (x x (- y 1)))))
                (lambda ( x y ) ( if ( = y 0) 1 (* y (x x (- y 1))))) 5)", as_int, 5is*4*3*2);
}
<commit_msg>cleanup<commit_after>use std::cell::RefCell;
use std::rc::Rc;

use ast::*;
use env::*;
use parser::*;

pub struct Evaler {
    parser: Parser,
    env:    Rc<RefCell<Env>>
}

#[allow(dead_code)]
impl Evaler {
    pub fn new() -> Evaler {
        let mut env = Env::new();
        Evaler {
            parser: Parser::new(),
            env:    Rc::new(RefCell::new(env))
        }
    }

    pub fn eval(&mut self, code: String) -> Option<ExprAst> {
        self.parser.load(code);
        let mut res = None;
        loop {
            let exp = self.parser.read_exp();
            match exp {
                Some(_exp) => {
                    let r = self.eval_exp(_exp);
                    r.print();
                    res = Some(r);
                },
                None => break
            }
        }
        res
    }

    fn eval_exp(&mut self, exp: ExprAst) -> ExprAst {
        if exp.is_self()   { return exp; }
        if exp.is_symbol() { return self.env.clone().borrow().lookup(exp).unwrap(); }
        if exp.is_quote()  { return exp.cdr().car(); }
        if exp.is_assign() { return self.eval_assign(exp); }
        if exp.is_def()    { return self.eval_def(exp); }
        if exp.is_begin()  { return self.eval_begin(exp); }
        if exp.is_if()     { return self.eval_if(exp); }
        if exp.is_lambda() { return self.eval_lambda(exp); }
        if exp.is_and()    { return self.eval_and(exp); }
        if exp.is_or()     { return self.eval_or(exp); }
        if exp.is_cond()   { return self.eval_cond(exp); }
        if exp.is_let()    { return self.eval_let(exp); }
        if exp.is_pair()   { return self.eval_app(exp); }
        ExprAst::Symbol(SymbolNode::new("OK"))
    }

    fn eval_assign(&mut self, exp: ExprAst) -> ExprAst {
        let var = exp.c("da");
        let val = self.eval_exp(exp.c("dda"));
        self.env.clone().borrow_mut().def_var(var, val);
        ExprAst::Symbol(SymbolNode::new("OK"))
    }

    fn eval_def(&mut self, exp: ExprAst) -> ExprAst {
        let var = exp.def_var();
        let val = exp.def_val();
        let val = self.eval_exp(val);
        self.env.clone().borrow_mut().def_var(var, val);
        ExprAst::Symbol(SymbolNode::new("OK"))
    }

    fn eval_if(&mut self, exp: ExprAst) -> ExprAst {
        let pred = exp.c("da");
        let blk_t = exp.c("dda");
        let blk_f = exp.c("ddd");
        let res = self.eval_exp(pred);
        if res.is_true() {
            self.eval_exp(blk_t)
        } else {
            if res.is_empty() {
                ExprAst::Bool(BoolNode::new(false))
            } else {
                self.eval_exp(blk_f.car())
            }
        }
    }

    fn eval_and(&mut self, exp: ExprAst) -> ExprAst {
        let mut elems = exp.cdr();
        if elems.is_empty() {
            return ExprAst::Bool(BoolNode::new(true));
        }
        loop {
            if elems.is_last() { break; }
            let res = self.eval_exp(elems.car());
            if res.is_false() {
                return ExprAst::Bool(BoolNode::new(false));
            }
            elems = elems.cdr();
        }
        self.eval_exp(elems.car())
    }

    fn eval_or(&mut self, exp: ExprAst) -> ExprAst {
        let mut elems = exp.cdr();
        if elems.is_empty() {
            return ExprAst::Bool(BoolNode::new(true));
        }
        loop {
            if elems.is_last() { break; }
            let res = self.eval_exp(elems.car());
            if res.is_true() {
                return ExprAst::Bool(BoolNode::new(true));
            }
            elems = elems.cdr();
        }
        self.eval_exp(elems.car())
    }

    fn eval_cond(&mut self, exp: ExprAst) -> ExprAst {
        let mut elems = exp.cdr().car();
        loop {
            if elems.is_empty() { break; }
            let cur = elems.car();
            let val = self.eval_exp(cur.clone());
            if val.is_true() || val == ExprAst::Symbol(SymbolNode::new("else")) {
                return self.eval_exp(cur.clone().cdr().car());
            }
            elems = elems.cdr();
        }
        ExprAst::Bool(BoolNode::new(true))
    }

    fn eval_let(&mut self, exp: ExprAst) -> ExprAst {
        fn bind_params(exp: ExprAst) -> ExprAst {
            if exp.is_empty() {
                ExprAst::Nil
            } else {
                ExprAst::Pair(PairNode::new(exp.c("aa"), bind_params(exp.cdr())))
            }
        }

        fn bind_args(exp: ExprAst) -> ExprAst {
            if exp.is_empty() {
                ExprAst::Nil
            } else {
                ExprAst::Pair(PairNode::new(exp.c("ada"), bind_args(exp.cdr())))
            }
        }

        let bindings = exp.c("da");
        let obj = ExprAst::Pair(PairNode::new(
            bind_params(bindings.clone()).make_lambda(exp.c("dd")),
            bind_args(bindings)));
        self.eval_exp(obj)
    }

    fn eval_begin(&mut self, exp: ExprAst) -> ExprAst {
        let mut _exp = exp.cdr();
        loop {
            if _exp.is_last() { break; }
            self.eval_exp(_exp.car());
            _exp = _exp.cdr();
        }
        self.eval_exp(_exp.car())
    }

    fn eval_values(&mut self, exprs: ExprAst) -> ExprAst {
        if exprs.is_empty() {
            ExprAst::Nil
        } else {
            let first = self.eval_exp(exprs.car());
            ExprAst::Pair(PairNode::new(first, self.eval_values(exprs.cdr())))
        }
    }

    fn eval_app(&mut self, expr: ExprAst) -> ExprAst {
        let _proc = self.eval_exp(expr.car());
        let _args = self.eval_values(expr.cdr());
        if _proc.is_proc() {
            let func = _proc.as_proc().func();
            return func(_args);
        } else {
            assert!(_proc.is_cproc());
            let _vars = _proc.params();
            self.env = self.env.clone().borrow_mut().extend(_vars, _args);
            let begin = ExprAst::Symbol(SymbolNode::new("begin"));
            let res = self.eval_exp(ExprAst::Pair(PairNode::new(begin, _proc.body())));
            self.env = self.env.clone().borrow_mut().parent().unwrap();
            res
        }
    }

    fn eval_lambda(&mut self, expr: ExprAst) -> ExprAst {
        // vars + body + env
        ExprAst::CompProc(CompProcNode::new(expr.c("da"), expr.c("dd"), self.env.clone()))
    }
}

#[test]
fn test_evaler() {
    macro_rules! test_case {
        ($test_str:expr, $expect_type:ident, $expect_val:expr) => { {
            let mut evaler = Evaler::new();
            let res = evaler.eval($test_str.to_string()).unwrap();
            if res.$expect_type() != $expect_val {
                assert!(false);
            }
        }}
    }
    test_case!("11", as_int, 11);
    test_case!("'a", as_str, "a");
    test_case!(r#""hello""#, as_str, "hello");
    test_case!("#t", as_bool, true);
    test_case!("(and #t #t)", as_bool, true);
    test_case!("(and #t #f)", as_bool, false);
    test_case!("(or #t #t)", as_bool, true);
    test_case!("(or #f #f)", as_bool, false);
    test_case!("(or #t #f)", as_bool, true);
    test_case!("(set! a 1)", as_str, "OK");
    test_case!("(integer? 1)", as_bool, true);
    test_case!("(boolean? #t)", as_bool, true);
    test_case!("(boolean? #f)", as_bool, true);
    test_case!("(integer? #t)", as_bool, false);
    test_case!("(+ 1 1)", as_int, 2);
    test_case!("(- 1 1)", as_int, 0);
    test_case!("(+ 1 1  1)", as_int, 3);
    test_case!("(+ 1 1 -1 -1)", as_int, 0);
    test_case!("(/ 2 1)", as_int, 2);
    test_case!("(* 2 2)", as_int, 4);
    test_case!("(> 2 1)", as_bool, true);
    test_case!("(> 1 2)", as_bool, false);
    test_case!("(> 1 1)", as_bool, false);
    test_case!("(- (+ 3 (* 8 5)) 1)", as_int, 42);
    test_case!("(eq? 1 1)", as_bool, true);
    test_case!("(eq? 1 2)", as_bool, false);
    test_case!("(eq? 1 #f)", as_bool, false);
    test_case!("(pair? '())", as_bool, true);
    test_case!("(> (+ 1 1) 0)", as_bool, true);
    test_case!("(if (> 1 0) 1 else 2)", as_int, 1);
    test_case!("(begin (set! a 1) a)", as_int, 1);
    test_case!("(let ((a 1)) (+ a 1))", as_int, 2);
    test_case!("(let ((a 1)) (> (+ a 1) 0))", as_bool, true);
    test_case!("(let ((a 1) (b 2)) (> a b))", as_bool, false);
    test_case!("(define (add a b) (+ a b))", as_str, "OK");
    test_case!("(cond ((eq? 1 1) 1) ((> 1 2) 2) ))", as_int, 1);
    test_case!("(begin (set! x 5) (set! x 4) (+ x 1))", as_int, 5);
    test_case!("(car '(1 2))", as_int, 1);
    test_case!("(car (cdr '(1 2)))", as_int, 2);
    test_case!("(car (cons 1 2))", as_int, 1);
    test_case!("(car (cons (pair? '(1)) pair? '()))", as_bool, true);
    test_case!("(car (cdr (cons 1 2)))", as_int, 2);
    test_case!("(car (car (cons (cons 2 3) (cons 1 2))))", as_int, 2);
    test_case!("((lambda (x) x) 1)", as_int, 1);
    test_case!("((lambda (x y) (+ x y )) 1 2)", as_int, 3);
    test_case!("(define add4 (let ((x 4)) (lambda (y) (+ x y))))", as_str, "OK");
    test_case!("(begin 1 2)", as_int, 2);
    test_case!("((lambda (x) x) 5)", as_int, 5);
    test_case!("(let ((fu (lambda (x) (+ x 1)))) (fu 1))", as_int, 2);
    test_case!("((lambda ( x y ) ( if ( = y 0) 1 (* y (x x (- y 1)))))
                (lambda ( x y ) ( if ( = y 0) 1 (* y (x x (- y 1))))) 5)", as_int, 5is*4*3*2);
}
<|endoftext|>


<commit_before>extern crate getopts;
use getopts::Options;
use std::env;
use std::fs::File;
use std::io;
use std::io::Read;
use std::mem;

fn pointer_size() -> usize {
    let tmp = 0u8;
    let boxed = Box::new(tmp);
    mem::size_of_val(&boxed)
}

fn read_blend_file(inp: &str) -> io::Result<()> {
    // open file
    let mut file = File::open(inp).unwrap();
    // read 12 bytes from the Blender file
    let mut buf = [0u8; 12];
    let bytes_read = file.read(&mut buf).unwrap();
    if bytes_read != buf.len() {
        println!("{} bytes read, but {} expected ...", bytes_read, buf.len());
        return Ok(());
    }
    let mut header = String::new();
    for e in buf.iter() {
        header.push(*e as char);
    }
    println!("header = \"{}\"", header);
    // compare first 7 chars to "BLENDER"
    if header.len() >= 7 {
        // create copy of header without moving value
        let mut blender = String::new();
        blender = blender + &header;
        blender.truncate(7); // first 7 chars
        if blender == "BLENDER" {
            println!("starts with \"BLENDER\" ...");
            // switch to byte copy of String representation
            let bytes = header.into_bytes();
            // check 8th char of header
            println!("8th char of header = '{}'", bytes[7] as char);
            // check for 32-bit pointers vs. 64-bit pointers
            let ptr_size = pointer_size();
            println!("size of pointer in bytes: {}", ptr_size);
            let mut ptr_size_differs = false;
            if bytes[7] as char == '_' {
                // 32-bit pointers expected
                if ptr_size != 4 {
                    ptr_size_differs = true;
                }
                println!("32-bit pointers in file, pointer size differs? {}",
                         ptr_size_differs);
            } else {
                // 64-bit pointers expected
                if ptr_size != 8 {
                    ptr_size_differs = true;
                }
                println!("64-bit pointers in file, pointer size differs? {}",
                         ptr_size_differs);
            }
            // little endian or big endian?
            let mut l_endian;
            let endian_test = 1u16; // value one stored in two bytes
            if endian_test.to_le() != endian_test {
                l_endian = false;
            } else {
                // if we are on a little endian machine to_le() is a no-op
                l_endian = true;
            }
            println!("l_endian = {}", l_endian);
            let mut switch_endian;
            if bytes[8] as char == 'v' {
                // file stores little endian
                if !l_endian {
                    switch_endian = true;
                } else {
                    switch_endian = false;
                }
            } else {
                // file stores big endian
                if l_endian {
                    switch_endian = true;
                } else {
                    switch_endian = false;
                }
            }
            println!("switch_endian = {}", switch_endian);
            // get the version number
            let last3c = vec!(bytes[9], bytes[10], bytes[11]);
            let version = String::from_utf8(last3c).unwrap(); // last 3 chars
            println!("version = {}", version);
            if ptr_size == 8 && (ptr_size_differs | switch_endian) == false {
                // assumes 64-bit pointers (in file as well as on platform)
                // we also don't handle endian switching (yet)
                return read_remaining_blend_file(file);
            } else {
                println!("TODO: ptr_size = {}", ptr_size);
                println!("TODO: ptr_size_differs = {}", ptr_size_differs);
                println!("TODO: switch_endian = {}", switch_endian);
            }
        } else {
            println!("ERROR: FILE is not a Blender file");
        }
    } else {
        println!("ERROR: FILE is not a Blender file");
    }
    Ok(())
}

fn read_remaining_blend_file(mut file: File) -> io::Result<()> {
    // read_file_dna
    // 4 * int + 64-bit pointer
    let mut buf = [0u8; 24];
    let bytes_read = file.read(&mut buf).unwrap();
    if bytes_read != buf.len() {
        println!("{} bytes read, but {} expected ...", bytes_read, buf.len());
        return Ok(());
    }
    let mut bhead = String::new();
    for e in buf.iter() {
        bhead.push(*e as char);
    }
    let mut code = String::new();
    code.push_str(&bhead); // copy
    code.truncate(4); // first 4 chars
    println!("code = {}", code);
    // first int is 'len'
    let mut len: u32 = 0;
    len += (buf[4] as u32) <<  0;
    len += (buf[5] as u32) <<  8;
    len += (buf[6] as u32) << 16;
    len += (buf[7] as u32) << 24;
    println!("len = {}", len);
    // WORK: read remaining file
    Ok(())
}

fn do_work(inp: &str, out: Option<String>) {
    println!("FILE = {}", inp);
    match out {
        Some(x) => println!("output file name: {}", x),
        None => println!("no output file name"),
    }
    match read_blend_file(inp) {
        Ok(_) => { ; }
        Err(f) => { panic!(f.to_string()) }
    };
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} FILE [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optopt("o", "", "set output file name", "NAME");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
    let output = matches.opt_str("o");
    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };
    do_work(&input, output);
}
<commit_msg>The next step is to use a loop and implement the 2 TODO branches.<commit_after>extern crate getopts;
use getopts::Options;
use std::env;
use std::fs::File;
use std::io;
use std::io::Read;
use std::mem;

fn pointer_size() -> usize {
    let tmp = 0u8;
    let boxed = Box::new(tmp);
    mem::size_of_val(&boxed)
}

fn read_blend_file(inp: &str) -> io::Result<()> {
    // open file
    let mut file = File::open(inp).unwrap();
    // read 12 bytes from the Blender file
    let mut buf = [0u8; 12];
    let bytes_read = file.read(&mut buf).unwrap();
    if bytes_read != buf.len() {
        println!("{} bytes read, but {} expected ...", bytes_read, buf.len());
        return Ok(());
    }
    let mut header = String::new();
    for e in buf.iter() {
        header.push(*e as char);
    }
    println!("header = \"{}\"", header);
    // compare first 7 chars to "BLENDER"
    if header.len() >= 7 {
        // create copy of header without moving value
        let mut blender = String::new();
        blender = blender + &header;
        blender.truncate(7); // first 7 chars
        if blender == "BLENDER" {
            println!("starts with \"BLENDER\" ...");
            // switch to byte copy of String representation
            let bytes = header.into_bytes();
            // check 8th char of header
            println!("8th char of header = '{}'", bytes[7] as char);
            // check for 32-bit pointers vs. 64-bit pointers
            let ptr_size = pointer_size();
            println!("size of pointer in bytes: {}", ptr_size);
            let mut ptr_size_differs = false;
            if bytes[7] as char == '_' {
                // 32-bit pointers expected
                if ptr_size != 4 {
                    ptr_size_differs = true;
                }
                println!("32-bit pointers in file, pointer size differs? {}",
                         ptr_size_differs);
            } else {
                // 64-bit pointers expected
                if ptr_size != 8 {
                    ptr_size_differs = true;
                }
                println!("64-bit pointers in file, pointer size differs? {}",
                         ptr_size_differs);
            }
            // little endian or big endian?
            let mut l_endian;
            let endian_test = 1u16; // value one stored in two bytes
            if endian_test.to_le() != endian_test {
                l_endian = false;
            } else {
                // if we are on a little endian machine to_le() is a no-op
                l_endian = true;
            }
            println!("l_endian = {}", l_endian);
            let mut switch_endian;
            if bytes[8] as char == 'v' {
                // file stores little endian
                if !l_endian {
                    switch_endian = true;
                } else {
                    switch_endian = false;
                }
            } else {
                // file stores big endian
                if l_endian {
                    switch_endian = true;
                } else {
                    switch_endian = false;
                }
            }
            println!("switch_endian = {}", switch_endian);
            // get the version number
            let last3c = vec!(bytes[9], bytes[10], bytes[11]);
            let version = String::from_utf8(last3c).unwrap(); // last 3 chars
            println!("version = {}", version);
            if ptr_size == 8 && (ptr_size_differs | switch_endian) == false {
                // assumes 64-bit pointers (in file as well as on platform)
                // we also don't handle endian switching (yet)
                return read_remaining_blend_file(file);
            } else {
                println!("TODO: ptr_size = {}", ptr_size);
                println!("TODO: ptr_size_differs = {}", ptr_size_differs);
                println!("TODO: switch_endian = {}", switch_endian);
            }
        } else {
            println!("ERROR: FILE is not a Blender file");
        }
    } else {
        println!("ERROR: FILE is not a Blender file");
    }
    Ok(())
}

fn read_remaining_blend_file(mut file: File) -> io::Result<()> {
    // read_file_dna
    // 4 * int + 64-bit pointer
    let mut buf = [0u8; 24];
    let bytes_read = file.read(&mut buf).unwrap();
    if bytes_read != buf.len() {
        println!("{} bytes read, but {} expected ...", bytes_read, buf.len());
        return Ok(());
    }
    let mut bhead = String::new();
    for e in buf.iter() {
        bhead.push(*e as char);
    }
    let mut code = String::new();
    code.push_str(&bhead); // copy
    code.truncate(4); // first 4 chars
    println!("code = {}", code);
    // first int is 'len'
    let mut len: u32 = 0;
    len += (buf[4] as u32) <<  0;
    len += (buf[5] as u32) <<  8;
    len += (buf[6] as u32) << 16;
    len += (buf[7] as u32) << 24;
    println!("len = {}", len);
    // TODO: if code == "ENDB" && len == 0 { ... }
    if code == "ENDB" && len == 0 {
        println!("TODO: code == \"{}\"", code);
    } else if code == "DNA1" {
        println!("TODO: code == \"{}\"", code);
    } else {
        let mut tc = String::new();
        tc.push_str(&bhead); // copy
        tc.truncate(2); // first 4 chars
        if tc == "CA" {
            println!("TODO: tc == \"{}\"", tc);
        } else {
            let mut dummy: Vec<u8> = Vec::with_capacity(len as usize);
            for i in 0..len {
                dummy.push(i as u8);
            }
            let mut buf = &mut dummy;
            let bytes_read = file.read(buf).unwrap();
            if bytes_read != buf.len() {
                println!("{} bytes read, but {} expected ...", bytes_read, buf.len());
                return Ok(());
            } else {
                println!("{} bytes read ...", bytes_read);
            }
        }
    }
    // WORK: read remaining file
    Ok(())
}

fn do_work(inp: &str, out: Option<String>) {
    println!("FILE = {}", inp);
    match out {
        Some(x) => println!("output file name: {}", x),
        None => println!("no output file name"),
    }
    match read_blend_file(inp) {
        Ok(_) => { ; }
        Err(f) => { panic!(f.to_string()) }
    };
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} FILE [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optopt("o", "", "set output file name", "NAME");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
    let output = matches.opt_str("o");
    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };
    do_work(&input, output);
}
<|endoftext|>


<commit_before>use ::response::*;
use ::error::*;

use std::str;

use curl::http as curl_http;

use rustc_serialize::json;
use rustc_serialize::Decodable;

/// The `API_ACCEPT_HEADER` value is specified under the Accept header,
/// to enforce the use of the supported GitHub API, which is version 3.
static API_ACCEPT_HEADER: &'static str = "application/vnd.github.v3+json";

/// A simplistic function that wraps around the behaviour of an
/// http get-request as defined in `curl`.
/// As the library gets more complete, a more complete and complex
/// approach might be needed.
pub fn get<R: Decodable>(user: &str, url: &str, opts: Option<Vec<(&str, &str)>>) -> Result<(Vec<R>, Response), ClientError> {
    // Creating an empty request with header info needed for all requests.
    let mut handle = curl_http::handle();
    let mut request = handle.get(url)
        .header("User-Agent", user).header("Accept", API_ACCEPT_HEADER);

    // In case extre header options are needed,
    // it can be defined and given via the `opts` parameter.
    if opts.is_some() {
        for (name, val) in opts.unwrap() {
            request = request.header(name, val);
        }
    }

    // Executing the actual request via curl and storing the response.
    let response = request.exec().unwrap();
    // Retrieving the status code from the response object.
    let status_code = response.get_code();

    // Decoding the header and body in a controlled fashion,
    // throwing an error in case something went wrong internally,
    // replacing a panic, or when a response was negative.
    if check_status_code(status_code) {
        match str::from_utf8(response.get_body()) {
            Ok(raw_body) => {
                match json::decode(raw_body) {
                    Ok(b) => {
                        let body: Vec<R> = b;
                        return Ok((body, Response::populate(response.get_headers())));
                    }
                    Err(e) => {
                        return InternalError::new(&format!("{}", e));
                    }
                };
            }
            Err(e) => {
                return InternalError::new(&format!("{}", e));
            }
        }
    } else {
        return RequestError::new(status_code, response.get_body());
    }
}
<commit_msg>Cleanup and flatten header/body decoding<commit_after>use ::response::*;
use ::error::*;

use std::str;

use curl::http as curl_http;

use rustc_serialize::json;
use rustc_serialize::Decodable;

/// The `API_ACCEPT_HEADER` value is specified under the Accept header,
/// to enforce the use of the supported GitHub API, which is version 3.
static API_ACCEPT_HEADER: &'static str = "application/vnd.github.v3+json";

/// A simplistic function that wraps around the behaviour of an
/// http get-request as defined in `curl`.
/// As the library gets more complete, a more complete and complex
/// approach might be needed.
pub fn get<R: Decodable>(user: &str, url: &str, opts: Option<Vec<(&str, &str)>>) -> Result<(Vec<R>, Response), ClientError> {
    // Creating an empty request with header info needed for all requests.
    let mut handle = curl_http::handle();
    let mut request = handle.get(url)
        .header("User-Agent", user).header("Accept", API_ACCEPT_HEADER);

    // In case extre header options are needed,
    // it can be defined and given via the `opts` parameter.
    if opts.is_some() {
        for (name, val) in opts.unwrap() {
            request = request.header(name, val);
        }
    }

    // Executing the actual request via curl and storing the response.
    let response = request.exec().unwrap();
    // Retrieving the status code from the response object.
    let status_code = response.get_code();

    // Decoding the header and body in a controlled fashion,
    // throwing an error in case something went wrong internally,
    // replacing a panic, or when a response was negative.
    if !check_status_code(status_code) {
        return RequestError::new(status_code, response.get_body());
    }
    let raw_body = match str::from_utf8(response.get_body()) {
        Ok(raw_body) => raw_body,
        Err(e) => return InternalError::new(&format!("{}", e)),
    };
    match json::decode(raw_body) {
        Ok(b) => {
            let body: Vec<R> = b;
            Ok((body, Response::populate(response.get_headers())))
        },
        Err(e) => InternalError::new(&format!("{}", e)),
    }
}
<|endoftext|>


<commit_before>// An item is line of text that read from `find` command or stdin together with
// the internal states, such as selected or not

use ansi::{ANSIParser, AnsiString};
use curses::attr_t;
use field::*;
use regex::Regex;
use std::borrow::Cow;
use std::cmp::Ordering;
use std::default::Default;
use std::sync::Arc;

// An item will store everything that one line input will need to be operated and displayed.
//
// What's special about an item?
// The simplest version of an item is a line of string, but things are getting more complex:
// - The conversion of lower/upper case is slow in rust, because it involds unicode.
// - We may need to interpret the ANSI codes in the text.
// - The text can be transformed and limited while searching.

// About the ANSI, we made assumption that it is linewise, that means no ANSI codes will affect
// more than one line.

#[derive(Debug)]
pub struct Item {
    // (num of run, number of index)
    index: (usize, usize),

    // The text that will be ouptut when user press `enter`
    orig_text: String,

    // The text that will shown into the screen. Can be transformed.
    text: AnsiString,

    // cache of the lower case version of text. To improve speed
    chars: Vec<char>,

    matching_ranges: Vec<(usize, usize)>,

    // For the transformed ANSI case, the output will need another transform.
    using_transform_fields: bool,
    ansi_enabled: bool,
}

impl<'a> Item {
    pub fn new(
        orig_text: String,
        ansi_enabled: bool,
        trans_fields: &[FieldRange],
        matching_fields: &[FieldRange],
        delimiter: &Regex,
        index: (usize, usize),
    ) -> Self {
        let using_transform_fields = !trans_fields.is_empty();

        //        transformed | ANSI             | output
        //------------------------------------------------------
        //                    +- T -> trans+ANSI | ANSI
        //                    |                  |
        //      +- T -> trans +- F -> trans      | orig
        // orig |                                |
        //      +- F -> orig  +- T -> ANSI     ==| ANSI
        //                    |                  |
        //                    +- F -> orig       | orig

        let mut ansi_parser: ANSIParser = Default::default();

        let text = if using_transform_fields && ansi_enabled {
            // ansi and transform
            ansi_parser.parse_ansi(&parse_transform_fields(delimiter, &orig_text, trans_fields))
        } else if using_transform_fields {
            // transformed, not ansi
            AnsiString{stripped: parse_transform_fields(delimiter, &orig_text, trans_fields), ansi_states: Vec::new()}
        } else if ansi_enabled {
            // not transformed, ansi
            ansi_parser.parse_ansi(&orig_text)
        } else {
            // normal case
            AnsiString::new_empty()
        };

        let mut ret = Item {
            index,
            orig_text,
            text,
            chars: Vec::new(),
            using_transform_fields: !trans_fields.is_empty(),
            matching_ranges: Vec::new(),
            ansi_enabled,
        };

        let chars: Vec<char> = if ret.get_text().as_bytes().is_ascii() {
            ret.get_text().as_bytes().iter().map(|&s| s as char).collect()
        } else {
            ret.get_text().chars().collect()
        };

        let matching_ranges = if !matching_fields.is_empty() {
            parse_matching_fields(delimiter, ret.get_text(), matching_fields)
        } else {
            vec![(0, chars.len())]
        };

        ret.chars = chars;
        ret.matching_ranges = matching_ranges;
        ret
    }

    pub fn get_text(&self) -> &str {
        if !self.using_transform_fields && !self.ansi_enabled {
            &self.orig_text
        } else {
            &self.text.stripped
        }
    }

    pub fn get_orig_text(&'a self) -> Cow<'a, str> {
        Cow::Borrowed(&self.orig_text)
    }

    pub fn get_text_struct(&self) -> Option<&AnsiString> {
        if !self.using_transform_fields && !self.ansi_enabled {
            None
        } else {
            Some(&self.text)
        }
    }

    pub fn get_output_text(&'a self) -> Cow<'a, str> {
        if self.using_transform_fields && self.ansi_enabled {
            let mut ansi_parser: ANSIParser = Default::default();
            let text = ansi_parser.parse_ansi(&self.orig_text);
            Cow::Owned(text.into_inner())
        } else if !self.using_transform_fields && self.ansi_enabled {
            Cow::Borrowed(&self.text.stripped)
        } else {
            Cow::Borrowed(&self.orig_text)
        }
    }

    pub fn get_chars(&self) -> &[char] {
        &self.chars
    }

    pub fn get_ansi_states(&self) -> &Vec<(usize, attr_t)> {
        &self.text.ansi_states
    }

    pub fn get_index(&self) -> usize {
        self.index.1
    }

    pub fn get_full_index(&self) -> (usize, usize) {
        self.index
    }

    pub fn get_matching_ranges(&self) -> &[(usize, usize)] {
        &self.matching_ranges
    }
}

impl Clone for Item {
    fn clone(&self) -> Item {
        Item {
            index: self.index,
            orig_text: self.orig_text.clone(),
            text: self.text.clone(),
            chars: self.chars.clone(),
            using_transform_fields: self.using_transform_fields,
            matching_ranges: self.matching_ranges.clone(),
            ansi_enabled: self.ansi_enabled,
        }
    }
}

// A bunch of items
pub type ItemGroup = Vec<Arc<Item>>;
pub type MatchedItemGroup = Vec<MatchedItem>;

pub type Rank = [i64; 4]; // score, index, start, end

#[derive(PartialEq, Eq, Clone, Debug)]
#[allow(dead_code)]
pub enum MatchedRange {
    Range(usize, usize),
    Chars(Vec<usize>),
}

#[derive(Clone, Debug)]
pub struct MatchedItem {
    pub item: Arc<Item>,
    pub rank: Rank,
    pub matched_range: Option<MatchedRange>, // range of chars that metched the pattern
}

impl MatchedItem {
    pub fn builder(item: Arc<Item>) -> Self {
        MatchedItem {
            item: item,
            rank: [0, 0, 0, 0],
            matched_range: None,
        }
    }

    pub fn matched_range(mut self, range: MatchedRange) -> Self {
        self.matched_range = Some(range);
        self
    }

    pub fn rank(mut self, rank: Rank) -> Self {
        self.rank = rank;
        self
    }

    pub fn build(self) -> Self {
        self
    }
}

impl Ord for MatchedItem {
    fn cmp(&self, other: &MatchedItem) -> Ordering {
        self.rank.cmp(&other.rank)
    }
}

// `PartialOrd` needs to be implemented as well.
impl PartialOrd for MatchedItem {
    fn partial_cmp(&self, other: &MatchedItem) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for MatchedItem {
    fn eq(&self, other: &MatchedItem) -> bool {
        self.rank == other.rank
    }
}

impl Eq for MatchedItem {}
<commit_msg>Remove unused function<commit_after>// An item is line of text that read from `find` command or stdin together with
// the internal states, such as selected or not

use ansi::{ANSIParser, AnsiString};
use curses::attr_t;
use field::*;
use regex::Regex;
use std::borrow::Cow;
use std::cmp::Ordering;
use std::default::Default;
use std::sync::Arc;

// An item will store everything that one line input will need to be operated and displayed.
//
// What's special about an item?
// The simplest version of an item is a line of string, but things are getting more complex:
// - The conversion of lower/upper case is slow in rust, because it involds unicode.
// - We may need to interpret the ANSI codes in the text.
// - The text can be transformed and limited while searching.

// About the ANSI, we made assumption that it is linewise, that means no ANSI codes will affect
// more than one line.

#[derive(Debug)]
pub struct Item {
    // (num of run, number of index)
    index: (usize, usize),

    // The text that will be ouptut when user press `enter`
    orig_text: String,

    // The text that will shown into the screen. Can be transformed.
    text: AnsiString,

    // cache of the lower case version of text. To improve speed
    chars: Vec<char>,

    matching_ranges: Vec<(usize, usize)>,

    // For the transformed ANSI case, the output will need another transform.
    using_transform_fields: bool,
    ansi_enabled: bool,
}

impl<'a> Item {
    pub fn new(
        orig_text: String,
        ansi_enabled: bool,
        trans_fields: &[FieldRange],
        matching_fields: &[FieldRange],
        delimiter: &Regex,
        index: (usize, usize),
    ) -> Self {
        let using_transform_fields = !trans_fields.is_empty();

        //        transformed | ANSI             | output
        //------------------------------------------------------
        //                    +- T -> trans+ANSI | ANSI
        //                    |                  |
        //      +- T -> trans +- F -> trans      | orig
        // orig |                                |
        //      +- F -> orig  +- T -> ANSI     ==| ANSI
        //                    |                  |
        //                    +- F -> orig       | orig

        let mut ansi_parser: ANSIParser = Default::default();

        let text = if using_transform_fields && ansi_enabled {
            // ansi and transform
            ansi_parser.parse_ansi(&parse_transform_fields(delimiter, &orig_text, trans_fields))
        } else if using_transform_fields {
            // transformed, not ansi
            AnsiString{stripped: parse_transform_fields(delimiter, &orig_text, trans_fields), ansi_states: Vec::new()}
        } else if ansi_enabled {
            // not transformed, ansi
            ansi_parser.parse_ansi(&orig_text)
        } else {
            // normal case
            AnsiString::new_empty()
        };

        let mut ret = Item {
            index,
            orig_text,
            text,
            chars: Vec::new(),
            using_transform_fields: !trans_fields.is_empty(),
            matching_ranges: Vec::new(),
            ansi_enabled,
        };

        let chars: Vec<char> = if ret.get_text().as_bytes().is_ascii() {
            ret.get_text().as_bytes().iter().map(|&s| s as char).collect()
        } else {
            ret.get_text().chars().collect()
        };

        let matching_ranges = if !matching_fields.is_empty() {
            parse_matching_fields(delimiter, ret.get_text(), matching_fields)
        } else {
            vec![(0, chars.len())]
        };

        ret.chars = chars;
        ret.matching_ranges = matching_ranges;
        ret
    }

    pub fn get_text(&self) -> &str {
        if !self.using_transform_fields && !self.ansi_enabled {
            &self.orig_text
        } else {
            &self.text.stripped
        }
    }

    pub fn get_orig_text(&'a self) -> Cow<'a, str> {
        Cow::Borrowed(&self.orig_text)
    }

    pub fn get_text_struct(&self) -> Option<&AnsiString> {
        if !self.using_transform_fields && !self.ansi_enabled {
            None
        } else {
            Some(&self.text)
        }
    }

    pub fn get_output_text(&'a self) -> Cow<'a, str> {
        if self.using_transform_fields && self.ansi_enabled {
            let mut ansi_parser: ANSIParser = Default::default();
            let text = ansi_parser.parse_ansi(&self.orig_text);
            Cow::Owned(text.into_inner())
        } else if !self.using_transform_fields && self.ansi_enabled {
            Cow::Borrowed(&self.text.stripped)
        } else {
            Cow::Borrowed(&self.orig_text)
        }
    }

    pub fn get_chars(&self) -> &[char] {
        &self.chars
    }

    pub fn get_index(&self) -> usize {
        self.index.1
    }

    pub fn get_full_index(&self) -> (usize, usize) {
        self.index
    }

    pub fn get_matching_ranges(&self) -> &[(usize, usize)] {
        &self.matching_ranges
    }
}

impl Clone for Item {
    fn clone(&self) -> Item {
        Item {
            index: self.index,
            orig_text: self.orig_text.clone(),
            text: self.text.clone(),
            chars: self.chars.clone(),
            using_transform_fields: self.using_transform_fields,
            matching_ranges: self.matching_ranges.clone(),
            ansi_enabled: self.ansi_enabled,
        }
    }
}

// A bunch of items
pub type ItemGroup = Vec<Arc<Item>>;
pub type MatchedItemGroup = Vec<MatchedItem>;

pub type Rank = [i64; 4]; // score, index, start, end

#[derive(PartialEq, Eq, Clone, Debug)]
#[allow(dead_code)]
pub enum MatchedRange {
    Range(usize, usize),
    Chars(Vec<usize>),
}

#[derive(Clone, Debug)]
pub struct MatchedItem {
    pub item: Arc<Item>,
    pub rank: Rank,
    pub matched_range: Option<MatchedRange>, // range of chars that metched the pattern
}

impl MatchedItem {
    pub fn builder(item: Arc<Item>) -> Self {
        MatchedItem {
            item: item,
            rank: [0, 0, 0, 0],
            matched_range: None,
        }
    }

    pub fn matched_range(mut self, range: MatchedRange) -> Self {
        self.matched_range = Some(range);
        self
    }

    pub fn rank(mut self, rank: Rank) -> Self {
        self.rank = rank;
        self
    }

    pub fn build(self) -> Self {
        self
    }
}

impl Ord for MatchedItem {
    fn cmp(&self, other: &MatchedItem) -> Ordering {
        self.rank.cmp(&other.rank)
    }
}

// `PartialOrd` needs to be implemented as well.
impl PartialOrd for MatchedItem {
    fn partial_cmp(&self, other: &MatchedItem) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for MatchedItem {
    fn eq(&self, other: &MatchedItem) -> bool {
        self.rank == other.rank
    }
}

impl Eq for MatchedItem {}
<|endoftext|>