<commit_before>use rustc::ty;
use rustc::ty::layout::{Align, LayoutOf, Size};
use rustc::hir::def_id::DefId;
use rustc::mir;
use syntax::attr;

use rand::RngCore;

use crate::*;

impl<'a, 'mir, 'tcx> EvalContextExt<'a, 'mir, 'tcx> for crate::MiriEvalContext<'a, 'mir, 'tcx> {}
pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'a, 'mir, 'tcx> {
    fn find_fn(
        &mut self,
        instance: ty::Instance<'tcx>,
        args: &[OpTy<'tcx, Tag>],
        dest: Option<PlaceTy<'tcx, Tag>>,
        ret: Option<mir::BasicBlock>,
    ) -> EvalResult<'tcx, Option<&'mir mir::Mir<'tcx>>> {
        let this = self.eval_context_mut();
        trace!("eval_fn_call: {:#?}, {:?}", instance, dest.map(|place| *place));

        // First, run the common hooks also supported by CTFE.
        if this.hook_fn(instance, args, dest)? {
            this.goto_block(ret)?;
            return Ok(None);
        }
        // There are some more lang items we want to hook that CTFE does not hook (yet).
        if this.tcx.lang_items().align_offset_fn() == Some(instance.def.def_id()) {
            // FIXME: return a real value in case the target allocation has an
            // alignment bigger than the one requested.
            let n = u128::max_value();
            let dest = dest.unwrap();
            let n = this.truncate(n, dest.layout);
            this.write_scalar(Scalar::from_uint(n, dest.layout.size), dest)?;
            this.goto_block(ret)?;
            return Ok(None);
        }

        // Try to see if we can do something about foreign items.
        if this.tcx.is_foreign_item(instance.def_id()) {
            // An external function that we cannot find MIR for, but we can still run enough
            // of them to make miri viable.
            this.emulate_foreign_item(instance.def_id(), args, dest, ret)?;
            // `goto_block` already handled.
            return Ok(None);
        }

        // Otherwise, load the MIR.
        Ok(Some(this.load_mir(instance.def)?))
    }

    fn malloc(
        &mut self,
        size: u64,
        zero_init: bool,
    ) -> Scalar<Tag> {
        let this = self.eval_context_mut();
        let tcx = &{this.tcx.tcx};
        if size == 0 {
            Scalar::from_int(0, this.pointer_size())
        } else {
            let align = this.tcx.data_layout.pointer_align.abi;
            let ptr = this.memory_mut().allocate(Size::from_bytes(size), align, MiriMemoryKind::C.into());
            if zero_init {
                // We just allocated this, the access cannot fail
                this.memory_mut()
                    .get_mut(ptr.alloc_id).unwrap()
                    .write_repeat(tcx, ptr, 0, Size::from_bytes(size)).unwrap();
            }
            Scalar::Ptr(ptr)
        }
    }

    fn free(
        &mut self,
        ptr: Scalar<Tag>,
    ) -> EvalResult<'tcx> {
        let this = self.eval_context_mut();
        if !ptr.is_null_ptr(this) {
            this.memory_mut().deallocate(
                ptr.to_ptr()?,
                None,
                MiriMemoryKind::C.into(),
            )?;
        }
        Ok(())
    }

    fn realloc(
        &mut self,
        old_ptr: Scalar<Tag>,
        new_size: u64,
    ) -> EvalResult<'tcx, Scalar<Tag>> {
        let this = self.eval_context_mut();
        let align = this.tcx.data_layout.pointer_align.abi;
        if old_ptr.is_null_ptr(this) {
            if new_size == 0 {
                Ok(Scalar::from_int(0, this.pointer_size()))
            } else {
                let new_ptr = this.memory_mut().allocate(
                    Size::from_bytes(new_size),
                    align,
                    MiriMemoryKind::C.into()
                );
                Ok(Scalar::Ptr(new_ptr))
            }
        } else {
            let old_ptr = old_ptr.to_ptr()?;
            let memory = this.memory_mut();
            let old_size = Size::from_bytes(memory.get(old_ptr.alloc_id)?.bytes.len() as u64);
            if new_size == 0 {
                memory.deallocate(
                    old_ptr,
                    Some((old_size, align)),
                    MiriMemoryKind::C.into(),
                )?;
                Ok(Scalar::from_int(0, this.pointer_size()))
            } else {
                let new_ptr = memory.reallocate(
                    old_ptr,
                    old_size,
                    align,
                    Size::from_bytes(new_size),
                    align,
                    MiriMemoryKind::C.into(),
                )?;
                Ok(Scalar::Ptr(new_ptr))
            }
        }
    }

    /// Emulates calling a foreign item, failing if the item is not supported.
    /// This function will handle `goto_block` if needed.
    fn emulate_foreign_item(
        &mut self,
        def_id: DefId,
        args: &[OpTy<'tcx, Tag>],
        dest: Option<PlaceTy<'tcx, Tag>>,
        ret: Option<mir::BasicBlock>,
    ) -> EvalResult<'tcx> {
        let this = self.eval_context_mut();
        let attrs = this.tcx.get_attrs(def_id);
        let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") {
            Some(name) => name.as_str(),
            None => this.tcx.item_name(def_id).as_str(),
        };
        // Strip linker suffixes (seen on 32-bit macOS).
        let link_name = link_name.get().trim_end_matches("$UNIX2003");
        let tcx = &{this.tcx.tcx};

        // First: functions that diverge.
        match link_name {
            "__rust_start_panic" | "panic_impl" => {
                return err!(MachineError("the evaluated program panicked".to_string()));
            }
            "exit" => {
                let code = this.read_scalar(args[0])?.to_i32()?;
                return err!(Exit(code));
            }
            "ExitProcess" => {
                let code = this.read_scalar(args[0])?.to_u32()?;
                return err!(Exit(code as i32));
            }
            _ => if dest.is_none() {
                return err!(Unimplemented(
                    format!("can't call diverging foreign function: {}", link_name),
                ));
            }
        }

        // Next: functions that assume a ret and dest.
        let dest = dest.expect("we already checked for a dest");
        let ret = ret.expect("dest is `Some` but ret is `None`");
        match link_name {
            "malloc" => {
                let size = this.read_scalar(args[0])?.to_usize(this)?;
                let res = this.malloc(size, /*zero_init:*/ false);
                this.write_scalar(res, dest)?;
            }
            "calloc" => {
                let items = this.read_scalar(args[0])?.to_usize(this)?;
                let len = this.read_scalar(args[1])?.to_usize(this)?;
                let size = items.checked_mul(len).ok_or_else(|| InterpError::Overflow(mir::BinOp::Mul))?;
                let res = this.malloc(size, /*zero_init:*/ true);
                this.write_scalar(res, dest)?;<commit_msg>unify code paths<commit_after>use rustc::ty;
use rustc::ty::layout::{Align, LayoutOf, Size};
use rustc::hir::def_id::DefId;
use rustc::mir;
use syntax::attr;

use rand::RngCore;

use crate::*;

impl<'a, 'mir, 'tcx> EvalContextExt<'a, 'mir, 'tcx> for crate::MiriEvalContext<'a, 'mir, 'tcx> {}
pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'a, 'mir, 'tcx> {
    fn find_fn(
        &mut self,
        instance: ty::Instance<'tcx>,
        args: &[OpTy<'tcx, Tag>],
        dest: Option<PlaceTy<'tcx, Tag>>,
        ret: Option<mir::BasicBlock>,
    ) -> EvalResult<'tcx, Option<&'mir mir::Mir<'tcx>>> {
        let this = self.eval_context_mut();
        trace!("eval_fn_call: {:#?}, {:?}", instance, dest.map(|place| *place));

        // First, run the common hooks also supported by CTFE.
        if this.hook_fn(instance, args, dest)? {
            this.goto_block(ret)?;
            return Ok(None);
        }
        // There are some more lang items we want to hook that CTFE does not hook (yet).
        if this.tcx.lang_items().align_offset_fn() == Some(instance.def.def_id()) {
            // FIXME: return a real value in case the target allocation has an
            // alignment bigger than the one requested.
            let n = u128::max_value();
            let dest = dest.unwrap();
            let n = this.truncate(n, dest.layout);
            this.write_scalar(Scalar::from_uint(n, dest.layout.size), dest)?;
            this.goto_block(ret)?;
            return Ok(None);
        }

        // Try to see if we can do something about foreign items.
        if this.tcx.is_foreign_item(instance.def_id()) {
            // An external function that we cannot find MIR for, but we can still run enough
            // of them to make miri viable.
            this.emulate_foreign_item(instance.def_id(), args, dest, ret)?;
            // `goto_block` already handled.
            return Ok(None);
        }

        // Otherwise, load the MIR.
        Ok(Some(this.load_mir(instance.def)?))
    }

    fn malloc(
        &mut self,
        size: u64,
        zero_init: bool,
    ) -> Scalar<Tag> {
        let this = self.eval_context_mut();
        let tcx = &{this.tcx.tcx};
        if size == 0 {
            Scalar::from_int(0, this.pointer_size())
        } else {
            let align = this.tcx.data_layout.pointer_align.abi;
            let ptr = this.memory_mut().allocate(Size::from_bytes(size), align, MiriMemoryKind::C.into());
            if zero_init {
                // We just allocated this, the access cannot fail
                this.memory_mut()
                    .get_mut(ptr.alloc_id).unwrap()
                    .write_repeat(tcx, ptr, 0, Size::from_bytes(size)).unwrap();
            }
            Scalar::Ptr(ptr)
        }
    }

    fn free(
        &mut self,
        ptr: Scalar<Tag>,
    ) -> EvalResult<'tcx> {
        let this = self.eval_context_mut();
        if !ptr.is_null_ptr(this) {
            this.memory_mut().deallocate(
                ptr.to_ptr()?,
                None,
                MiriMemoryKind::C.into(),
            )?;
        }
        Ok(())
    }

    fn realloc(
        &mut self,
        old_ptr: Scalar<Tag>,
        new_size: u64,
    ) -> EvalResult<'tcx, Scalar<Tag>> {
        let this = self.eval_context_mut();
        let align = this.tcx.data_layout.pointer_align.abi;
        if old_ptr.is_null_ptr(this) {
            if new_size == 0 {
                Ok(Scalar::from_int(0, this.pointer_size()))
            } else {
                let new_ptr = this.memory_mut().allocate(
                    Size::from_bytes(new_size),
                    align,
                    MiriMemoryKind::C.into()
                );
                Ok(Scalar::Ptr(new_ptr))
            }
        } else {
            let old_ptr = old_ptr.to_ptr()?;
            let memory = this.memory_mut();
            let old_size = Size::from_bytes(memory.get(old_ptr.alloc_id)?.bytes.len() as u64);
            if new_size == 0 {
                memory.deallocate(
                    old_ptr,
                    Some((old_size, align)),
                    MiriMemoryKind::C.into(),
                )?;
                Ok(Scalar::from_int(0, this.pointer_size()))
            } else {
                let new_ptr = memory.reallocate(
                    old_ptr,
                    old_size,
                    align,
                    Size::from_bytes(new_size),
                    align,
                    MiriMemoryKind::C.into(),
                )?;
                Ok(Scalar::Ptr(new_ptr))
            }
        }
    }

    /// Emulates calling a foreign item, failing if the item is not supported.
    /// This function will handle `goto_block` if needed.
    fn emulate_foreign_item(
        &mut self,
        def_id: DefId,
        args: &[OpTy<'tcx, Tag>],
        dest: Option<PlaceTy<'tcx, Tag>>,
        ret: Option<mir::BasicBlock>,
    ) -> EvalResult<'tcx> {
        let this = self.eval_context_mut();
        let attrs = this.tcx.get_attrs(def_id);
        let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") {
            Some(name) => name.as_str(),
            None => this.tcx.item_name(def_id).as_str(),
        };
        // Strip linker suffixes (seen on 32-bit macOS).
        let link_name = link_name.get().trim_end_matches("$UNIX2003");
        let tcx = &{this.tcx.tcx};

        // First: functions that diverge.
        match link_name {
            "__rust_start_panic" | "panic_impl" => {
                return err!(MachineError("the evaluated program panicked".to_string()));
            }
            "exit" | "ExitProcess" => {
                // it's really u32 for ExitProcess, but we have to put it into the `Exit` error variant anyway
                let code = this.read_scalar(args[0])?.to_i32()?;
                return err!(Exit(code));
            }
            _ => if dest.is_none() {
                return err!(Unimplemented(
                    format!("can't call diverging foreign function: {}", link_name),
                ));
            }
        }

        // Next: functions that assume a ret and dest.
        let dest = dest.expect("we already checked for a dest");
        let ret = ret.expect("dest is `Some` but ret is `None`");
        match link_name {
            "malloc" => {
                let size = this.read_scalar(args[0])?.to_usize(this)?;
                let res = this.malloc(size, /*zero_init:*/ false);
                this.write_scalar(res, dest)?;
            }
            "calloc" => {
                let items = this.read_scalar(args[0])?.to_usize(this)?;
                let len = this.read_scalar(args[1])?.to_usize(this)?;
                let size = items.checked_mul(len).ok_or_else(|| InterpError::Overflow(mir::BinOp::Mul))?;
                let res = this.malloc(size, /*zero_init:*/ true);
                this.write_scalar(res, dest)?;
{'fraction_non_alphanumeric': 0.13474212034383953, 'fraction_numerical': 0.004512893982808023, 'mean_word_length': 1.6894625313041802, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 76, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5646604', 'n_tokens_mistral': 4256, 'n_tokens_neox': 4203, 'n_words': 933}