<commit_before>// Semantics analyzer. This pass plays a few important roles as shown
// below:
//
// - Add types to nodes. For example, a tree that represents "1+2" is
//   typed as INT because the result type of an addition of two
//   integers is integer.
//
// - Insert nodes to make array-to-pointer conversion explicit.
//   Recall that, in C, "array of T" is automatically converted to
//   "pointer to T" in most contexts.
//
// - Scales operands for pointer arithmetic. E.g. ptr+1 becomes ptr+4
//   for integer and becomes ptr+8 for pointer.
//
// - Reject bad assignments, such as `1=2+3`.

use crate::parse::*;
use crate::token::*;
use crate::util::*;
use std::cell::RefCell;
use std::rc::Rc;

fn maybe_decay(base: & mut Node, decay: bool) -> & mut Node {
    if !decay || base.ty.borrow().ty != CType::ARY {
        return base;
    }

    let tmp = base.clone();
    *base = alloc_node();
    base.op = NodeType::ADDR;

    let node_ty = tmp.clone().ty;
    let ty_tmp = *node_ty.borrow().clone().ary_of.unwrap();
    base.ty = Rc::new(RefCell::new(ptr_to(Rc::new(RefCell::new(ty_tmp)))));

    base.expr = Some(Box::new(tmp.clone()));
    base.token = tmp.token.clone();
    return base;
}

macro_rules! bad_node {
    ($node:expr, $msg:expr) => {
        bad_token(&*$node.token.clone().unwrap(), $msg.to_string());
        panic!();
    };
}

fn check_lval(node: Box<Node>) {
    match node.op {
        NodeType::VAR | NodeType::DEREF | NodeType::DOT => { }
        _ => {
            bad_node!(node, "not an lvalue");
        }
    }
}

fn scale_ptr(op: NodeType, base: Node, ty: Type) -> Node {
    let mut node = alloc_node();
    node.op = op;
    node.lhs = Some(Box::new(base.clone()));
    let ptr = ty.ptr_to.unwrap();
    node.rhs = Some(Box::new(new_int_node(ptr.borrow().size, base.token.clone())));
    node.token = base.token.clone();
    return node;
}

fn check_int(node: & Node) {
    if node.ty.borrow().ty != CType::INT {
        if node.ty.borrow().ty != CType::CHAR {
            bad_node!(node, "not an integer");
        }
    }
}

fn walk<'a>(node: &'a mut Node, prog: &'a mut Program) -> &'a Node {
    return do_walk(node, true, prog);
}

fn walk_nodecay<'a>(node: &'a mut Node, prog: &'a mut Program) -> &'a Node {
    return do_walk(node, false, prog);
}

fn do_walk<'a>(node: &'a mut Node, decay: bool, prog: &'a mut Program) -> &'a Node {
    match node.op {
        NodeType::NUM |
            NodeType::NULL |
            NodeType::BREAK |
            NodeType::CONTINUE => {
            return node;
        }
        NodeType::VAR => {
            return maybe_decay(node, decay);
        }
        NodeType::VARDEF => {
            match node.init {
                Some(_) => {
                    node.init = Some(Box::new(walk(&mut *node.init.clone().unwrap(), prog).clone()));
                }
                None => { }
            }
            return node;
        }
        NodeType::IF => {
            node.cond = Some(Box::new(walk(&mut *node.cond.clone().unwrap(), prog).clone()));
            node.then = Some(Box::new(walk(&mut *node.then.clone().unwrap(), prog).clone()));

            if node.els.is_some() {
                node.els = Some(Box::new(walk(&mut *node.els.clone().unwrap(), prog).clone()));
            }
            return node;
        }
        NodeType::FOR => {
            node.init = Some(Box::new(walk(&mut *node.init.clone().unwrap(), prog).clone()));
            if let Some(mut cond) = node.cond.clone() {
                node.cond = Some(Box::new(walk(&mut cond, prog).clone()));
            }
            if let Some(mut inc) = node.inc.clone() {
                node.inc = Some(Box::new(walk(&mut inc, prog).clone()));
            }
            node.body = Some(Box::new(walk(&mut *node.body.clone().unwrap(), prog).clone()));
            return node;
        }
        NodeType::DO_WHILE => {
            node.cond = Some(Box::new(walk(&mut *node.cond.clone().unwrap(), prog).clone()));
            node.body = Some(Box::new(walk(&mut *node.body.clone().unwrap(), prog).clone()));
            return node;
        }
        NodeType::ADD => {
            node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
            node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));

            match (&mut node.lhs, &mut node.rhs) {
                (Some(ref mut lhs), Some(ref mut rhs)) => {
                    if rhs.ty.borrow().ty == CType::PTR {
                        //use std::mem;
                        //mem::swap(lhs, rhs);
                        let n = lhs.clone();
                        *lhs = rhs.clone();
                        *rhs = n;
                    }
                }
                _ => {}
            }

            if let Some(ref rhs) = node.rhs {
                check_int(rhs);
            }

            if let Some(ref lhs) = node.lhs {
                if lhs.ty.borrow().ty == CType::PTR {
                    node.rhs = Some(Box::new(scale_ptr(NodeType::MUL, *node.rhs.clone().unwrap(), lhs.ty.borrow().clone())));
                }
            }

            if let Some(ref lhs) = node.lhs {
                node.ty = lhs.ty.clone();
            }
            return node;
        }
        NodeType::SUB => {
            node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
            node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));

            let l = &node.rhs.clone().unwrap().ty;
            let r = &node.lhs.clone().unwrap().ty;
            if l.borrow().ty == CType::PTR && r.borrow().ty == CType::PTR {
                if !same_type(r.clone(), l.clone()) {
                    bad_node!(node, "incompatible pointer");
                }
                *node = scale_ptr(NodeType::DIV, node.clone(), l.borrow().clone());
            }

            node.ty = node.lhs.clone().unwrap().ty;
            return node;
        }
        NodeType::ADD_EQ |
            NodeType::SUB_EQ => {
                node.lhs = Some(Box::new(walk_nodecay(&mut *node.lhs.clone().unwrap(), prog).clone()));
                check_lval(node.lhs.clone().unwrap());
                node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));
                if let Some(ref lhs) = node.lhs {
                    node.ty = lhs.ty.clone();
                }

                if let Some(ref lhs) = node.lhs {
                    if lhs.ty.borrow().ty == CType::PTR {
                        node.rhs = Some(Box::new(scale_ptr(NodeType::MUL, *node.rhs.clone().unwrap(), lhs.ty.borrow().clone())));
                    }
                }
                return node;
        }

        NodeType::EQL |
            NodeType::MUL_EQ |
            NodeType::DIV_EQ |
            NodeType::MOD_EQ |
            NodeType::SHL_EQ |
            NodeType::SHR_EQ |
            NodeType::AND_EQ |
            NodeType::XOR_EQ |
            NodeType::OR_EQ => {
            node.lhs = Some(Box::new(walk_nodecay(&mut *node.lhs.clone().unwrap(), prog).clone()));
            check_lval(node.lhs.clone().unwrap());

            node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));
            node.ty = node.lhs.clone().unwrap().ty;
            return node;
        }
        NodeType::DOT => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            let node_ty = node.expr.clone().unwrap().ty;
            if node_ty.borrow().ty != CType::STRUCT {
                bad_node!(node, "struct expected before '.'");
            }

            let ty = node.expr.clone().unwrap().ty;
            if ty.borrow().members == None {
                bad_node!(node, format!("incomplete type: {:?}", node.expr));
            }

            for m in ty.borrow().clone().members.unwrap().iter() {
                if m.name != node.name {
                    continue;
                }

                node.ty = m.ty.clone();
                return maybe_decay(node, decay);
            }
            bad_node!(node, format!("member missing: {}", node.name));
        }
        NodeType::QUEST => {
            node.cond = Some(Box::new(walk(&mut *node.cond.clone().unwrap(), prog).clone()));
            node.then = Some(Box::new(walk(&mut *node.then.clone().unwrap(), prog).clone()));
            node.els = Some(Box::new(walk(&mut *node.els.clone().unwrap(), prog).clone()));
            node.ty = node.then.clone().unwrap().ty;
            return node;
        }
        NodeType::MUL |
            NodeType::DIV |
            NodeType::MOD |
            NodeType::LT |
            NodeType::OR |
            NodeType::XOR |
            NodeType::AND |
            NodeType::EQ |
            NodeType::NE |
            NodeType::LE |
            NodeType::SHL |
            NodeType::SHR |
            NodeType::LOGAND |
            NodeType::LOGOR => {
                node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
                node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));

                check_int(&*node.lhs.clone().unwrap());
                check_int(&*node.rhs.clone().unwrap());

                match node.lhs {
                    Some(ref lhs) => {
                        node.ty = lhs.ty.clone();
                    }
                    None => { }
                }
                node.ty = Rc::new(RefCell::new(int_ty()));
                return node;
            }
        NodeType::COMMA => {
            node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
            node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));
            node.ty = node.rhs.clone().unwrap().ty;
            return node;
        }
        NodeType::POST_INC | NodeType::POST_DEC => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            node.ty = node.expr.clone().unwrap().ty;
            return node;
        }
        NodeType::EXCLAM | NodeType::NOT => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            check_int(&*node.expr.clone().unwrap());
            node.ty = node.expr.clone().unwrap().ty;
            return node;
        }
        NodeType::ADDR => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            check_lval(node.expr.clone().unwrap());
            node.ty = Rc::new(RefCell::new(ptr_to(node.expr.clone().unwrap().ty)));
            return node;
        }
        NodeType::DEREF => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));

            match &mut node.expr {
                Some(ref expr) => {
                    if expr.ty.borrow().ty != CType::PTR {
                        bad_node!(node, format!("operand must be a pointer: {:?}", expr));
                    }

                    let ptr = expr.ty.borrow().ptr_to.clone().unwrap();
                    if ptr.borrow().ty == CType::VOID {
                        bad_node!(node, "operand dereference void pointer");
                    }
                }
                None => {}
            }

            let ty_tmp = node.expr.clone().unwrap().ty;
            node.ty = ty_tmp.borrow().clone().ptr_to.unwrap();
            return maybe_decay(node, decay);
        }
        NodeType::RETURN |
            NodeType::EXPR_STMT => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            return node;
        }
        NodeType::CALL => {
            for i in 0..node.args.len() {
                node.args[i] = walk(&mut node.args[i], prog).clone();
            }
            let node_ty = *node.ty.borrow().clone().returning.unwrap();
            node.ty = Rc::new(RefCell::new(node_ty));
            return node;
        }
        NodeType::COMP_STMT => {
            for i in 0..node.stmts.len() {
                node.stmts[i] = walk(&mut node.stmts[i], prog).clone();
            }
            return node;
        }
        NodeType::STMT_EXPR => {
            let mut body = walk(&mut *node.body.clone().unwrap(), prog).clone();

            if body.stmts.len() == 0 {
                bad_node!(node, "empty statement expression");
            }

            let n = body.stmts.pop().unwrap();
            if n.op != NodeType::EXPR_STMT {
                bad_node!(node, "statement expression returning void");
            }

            node.body = Some(Box::new(body));
            node.expr = n.expr.clone();
            node.ty = n.expr.clone().unwrap().ty;
            return node;
        }
        _ => {
            panic!("unknown node type: {:?}", node);
        }
    }
}

pub fn get_type(node: &mut Node) -> Type {
    let mut prog = new_program();
    return walk_nodecay(node, &mut prog).ty.borrow().clone();
}

pub fn sema(prog: &mut Program) {
    let mut funcs = prog.funcs.clone();

    for func in funcs.iter_mut() {
        let node = func.node.clone();
        if node.borrow().op == NodeType::DECL {
            continue;
        }

        assert!(node.borrow().op == NodeType::FUNC);

        let mut args = Vec::new();
        for a in node.borrow_mut().args.iter_mut() {
            args.push(walk(&mut a.clone(), prog).clone());
        }
        node.borrow_mut().args = args;

        let mut body = node.borrow_mut().body.clone().unwrap();
        node.borrow_mut().body = Some(Box::new(walk(&mut body, prog).clone()));

        let mut off = 0;
        for v in func.lvars.iter_mut() {
            off = roundup(off, v.borrow().ty.align);
            off += v.borrow().ty.size;
            v.borrow_mut().offset = off;
        }
        func.stacksize = off;
    }

    prog.funcs = funcs;
}
<commit_msg>temporary<commit_after>// Semantics analyzer. This pass plays a few important roles as shown
// below:
//
// - Add types to nodes. For example, a tree that represents "1+2" is
//   typed as INT because the result type of an addition of two
//   integers is integer.
//
// - Insert nodes to make array-to-pointer conversion explicit.
//   Recall that, in C, "array of T" is automatically converted to
//   "pointer to T" in most contexts.
//
// - Scales operands for pointer arithmetic. E.g. ptr+1 becomes ptr+4
//   for integer and becomes ptr+8 for pointer.
//
// - Reject bad assignments, such as `1=2+3`.

use crate::parse::*;
use crate::token::*;
use crate::util::*;
use std::cell::RefCell;
use std::rc::Rc;

fn maybe_decay(base: & mut Node, decay: bool) -> & mut Node {
    if !decay || base.ty.borrow().ty != CType::ARY {
        return base;
    }

    let tmp = base.clone();
    *base = alloc_node();
    base.op = NodeType::ADDR;

    let node_ty = tmp.clone().ty;
    let ty_tmp = *node_ty.borrow().clone().ary_of.unwrap();
    base.ty = Rc::new(RefCell::new(ptr_to(Rc::new(RefCell::new(ty_tmp)))));

    base.expr = Some(Box::new(tmp.clone()));
    base.token = tmp.token.clone();
    return base;
}

macro_rules! bad_node {
    ($node:expr, $msg:expr) => {
        bad_token(&*$node.token.clone().unwrap(), $msg.to_string());
        panic!();
    };
}

fn check_lval(node: Box<Node>) {
    match node.op {
        NodeType::VAR | NodeType::DEREF | NodeType::DOT => { }
        _ => {
            bad_node!(node, "not an lvalue");
        }
    }
}

fn scale_ptr(op: NodeType, base: Node, ty: Type) -> Node {
    let mut node = alloc_node();
    node.op = op;
    node.lhs = Some(Box::new(base.clone()));
    let ptr = ty.ptr_to.unwrap();
    node.rhs = Some(Box::new(new_int_node(ptr.borrow().size, base.token.clone())));
    node.token = base.token.clone();
    return node;
}

fn check_int(node: & Node) {
    if node.ty.borrow().ty != CType::INT {
        if node.ty.borrow().ty != CType::CHAR {
            bad_node!(node, "not an integer");
        }
    }
}

fn walk<'a>(node: &'a mut Node, prog: &'a mut Program) -> &'a Node {
    return do_walk(node, true, prog);
}

fn walk_nodecay<'a>(node: &'a mut Node, prog: &'a mut Program) -> &'a Node {
    return do_walk(node, false, prog);
}

fn do_walk<'a>(node: &'a mut Node, decay: bool, prog: &'a mut Program) -> &'a Node {
    match node.op {
        NodeType::NUM |
            NodeType::NULL |
            NodeType::BREAK |
            NodeType::CONTINUE => {
            return node;
        }
        NodeType::VAR => {
            return maybe_decay(node, decay);
        }
        NodeType::VARDEF => {
            match node.init {
                Some(_) => {
                    node.init = Some(Box::new(walk(&mut *node.init.clone().unwrap(), prog).clone()));
                }
                None => { }
            }
            return node;
        }
        NodeType::IF => {
            node.cond = Some(Box::new(walk(&mut *node.cond.clone().unwrap(), prog).clone()));
            node.then = Some(Box::new(walk(&mut *node.then.clone().unwrap(), prog).clone()));

            if node.els.is_some() {
                node.els = Some(Box::new(walk(&mut *node.els.clone().unwrap(), prog).clone()));
            }
            return node;
        }
        NodeType::FOR => {
            node.init = Some(Box::new(walk(&mut *node.init.clone().unwrap(), prog).clone()));
            if let Some(mut cond) = node.cond.clone() {
                node.cond = Some(Box::new(walk(&mut cond, prog).clone()));
            }
            if let Some(mut inc) = node.inc.clone() {
                node.inc = Some(Box::new(walk(&mut inc, prog).clone()));
            }
            node.body = Some(Box::new(walk(&mut *node.body.clone().unwrap(), prog).clone()));
            return node;
        }
        NodeType::DO_WHILE => {
            node.cond = Some(Box::new(walk(&mut *node.cond.clone().unwrap(), prog).clone()));
            node.body = Some(Box::new(walk(&mut *node.body.clone().unwrap(), prog).clone()));
            return node;
        }
        NodeType::ADD => {
            node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
            node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));

            match (&mut node.lhs, &mut node.rhs) {
                (Some(ref mut lhs), Some(ref mut rhs)) => {
                    if rhs.ty.borrow().ty == CType::PTR {
                        //use std::mem;
                        //mem::swap(lhs, rhs);
                        let n = lhs.clone();
                        *lhs = rhs.clone();
                        *rhs = n;
                    }
                }
                _ => {}
            }

            if let Some(ref rhs) = node.rhs {
                check_int(rhs);
            }

            if let Some(ref lhs) = node.lhs {
                if lhs.ty.borrow().ty == CType::PTR {
                    node.rhs = Some(Box::new(scale_ptr(NodeType::MUL, *node.rhs.clone().unwrap(), lhs.ty.borrow().clone())));
                    node.ty = node.lhs.clone().unwrap().ty;
                } else {
                    node.ty = Rc::new(RefCell::new(int_ty()));
                }
            }

            return node;
        }
        NodeType::SUB => {
            node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
            node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));

            let l = &node.rhs.clone().unwrap().ty;
            let r = &node.lhs.clone().unwrap().ty;
            if l.borrow().ty == CType::PTR && r.borrow().ty == CType::PTR {
                if !same_type(r.clone(), l.clone()) {
                    bad_node!(node, "incompatible pointer");
                }
                *node = scale_ptr(NodeType::DIV, node.clone(), l.borrow().clone());
            }

            node.ty = node.lhs.clone().unwrap().ty;
            return node;
        }
        NodeType::ADD_EQ |
            NodeType::SUB_EQ => {
                node.lhs = Some(Box::new(walk_nodecay(&mut *node.lhs.clone().unwrap(), prog).clone()));
                check_lval(node.lhs.clone().unwrap());
                node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));
                if let Some(ref lhs) = node.lhs {
                    node.ty = lhs.ty.clone();
                }

                if let Some(ref lhs) = node.lhs {
                    if lhs.ty.borrow().ty == CType::PTR {
                        node.rhs = Some(Box::new(scale_ptr(NodeType::MUL, *node.rhs.clone().unwrap(), lhs.ty.borrow().clone())));
                    }
                }
                return node;
        }

        NodeType::EQL |
            NodeType::MUL_EQ |
            NodeType::DIV_EQ |
            NodeType::MOD_EQ |
            NodeType::SHL_EQ |
            NodeType::SHR_EQ |
            NodeType::AND_EQ |
            NodeType::XOR_EQ |
            NodeType::OR_EQ => {
            node.lhs = Some(Box::new(walk_nodecay(&mut *node.lhs.clone().unwrap(), prog).clone()));
            check_lval(node.lhs.clone().unwrap());

            node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));
            node.ty = node.lhs.clone().unwrap().ty;
            return node;
        }
        NodeType::DOT => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            let node_ty = node.expr.clone().unwrap().ty;
            if node_ty.borrow().ty != CType::STRUCT {
                bad_node!(node, "struct expected before '.'");
            }

            let ty = node.expr.clone().unwrap().ty;
            if ty.borrow().members == None {
                bad_node!(node, format!("incomplete type: {:?}", node.expr));
            }

            for m in ty.borrow().clone().members.unwrap().iter() {
                if m.name != node.name {
                    continue;
                }

                node.ty = m.ty.clone();
                return maybe_decay(node, decay);
            }
            bad_node!(node, format!("member missing: {}", node.name));
        }
        NodeType::QUEST => {
            node.cond = Some(Box::new(walk(&mut *node.cond.clone().unwrap(), prog).clone()));
            node.then = Some(Box::new(walk(&mut *node.then.clone().unwrap(), prog).clone()));
            node.els = Some(Box::new(walk(&mut *node.els.clone().unwrap(), prog).clone()));
            node.ty = node.then.clone().unwrap().ty;
            return node;
        }
        NodeType::MUL |
            NodeType::DIV |
            NodeType::MOD |
            NodeType::LT |
            NodeType::OR |
            NodeType::XOR |
            NodeType::AND |
            NodeType::EQ |
            NodeType::NE |
            NodeType::LE |
            NodeType::SHL |
            NodeType::SHR |
            NodeType::LOGAND |
            NodeType::LOGOR => {
                node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
                node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));

                check_int(&*node.lhs.clone().unwrap());
                check_int(&*node.rhs.clone().unwrap());

                match node.lhs {
                    Some(ref lhs) => {
                        node.ty = lhs.ty.clone();
                    }
                    None => { }
                }
                node.ty = Rc::new(RefCell::new(int_ty()));
                return node;
            }
        NodeType::COMMA => {
            node.lhs = Some(Box::new(walk(&mut *node.lhs.clone().unwrap(), prog).clone()));
            node.rhs = Some(Box::new(walk(&mut *node.rhs.clone().unwrap(), prog).clone()));
            node.ty = node.rhs.clone().unwrap().ty;
            return node;
        }
        NodeType::POST_INC | NodeType::POST_DEC => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            node.ty = node.expr.clone().unwrap().ty;
            return node;
        }
        NodeType::EXCLAM | NodeType::NOT => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            check_int(&*node.expr.clone().unwrap());
            node.ty = node.expr.clone().unwrap().ty;
            return node;
        }
        NodeType::ADDR => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            check_lval(node.expr.clone().unwrap());
            node.ty = Rc::new(RefCell::new(ptr_to(node.expr.clone().unwrap().ty)));
            return node;
        }
        NodeType::DEREF => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));

            match &mut node.expr {
                Some(ref expr) => {
                    if expr.ty.borrow().ty != CType::PTR {
                        bad_node!(node, format!("operand must be a pointer: {:?}", expr));
                    }

                    let ptr = expr.ty.borrow().ptr_to.clone().unwrap();
                    if ptr.borrow().ty == CType::VOID {
                        bad_node!(node, "operand dereference void pointer");
                    }
                }
                None => {}
            }

            let ty_tmp = node.expr.clone().unwrap().ty;
            node.ty = ty_tmp.borrow().clone().ptr_to.unwrap();
            return maybe_decay(node, decay);
        }
        NodeType::RETURN |
            NodeType::EXPR_STMT => {
            node.expr = Some(Box::new(walk(&mut *node.expr.clone().unwrap(), prog).clone()));
            return node;
        }
        NodeType::CALL => {
            for i in 0..node.args.len() {
                node.args[i] = walk(&mut node.args[i], prog).clone();
            }
            let node_ty = *node.ty.borrow().clone().returning.unwrap();
            node.ty = Rc::new(RefCell::new(node_ty));
            return node;
        }
        NodeType::COMP_STMT => {
            for i in 0..node.stmts.len() {
                node.stmts[i] = walk(&mut node.stmts[i], prog).clone();
            }
            return node;
        }
        NodeType::STMT_EXPR => {
            let mut body = walk(&mut *node.body.clone().unwrap(), prog).clone();

            if body.stmts.len() == 0 {
                bad_node!(node, "empty statement expression");
            }

            let n = body.stmts.pop().unwrap();
            if n.op != NodeType::EXPR_STMT {
                bad_node!(node, "statement expression returning void");
            }

            node.body = Some(Box::new(body));
            node.expr = n.expr.clone();
            node.ty = n.expr.clone().unwrap().ty;
            return node;
        }
        _ => {
            panic!("unknown node type: {:?}", node);
        }
    }
}

pub fn get_type(node: &mut Node) -> Type {
    let mut prog = new_program();
    return walk_nodecay(node, &mut prog).ty.borrow().clone();
}

pub fn sema(prog: &mut Program) {
    let mut funcs = prog.funcs.clone();

    for func in funcs.iter_mut() {
        let node = func.node.clone();
        if node.borrow().op == NodeType::DECL {
            continue;
        }

        assert!(node.borrow().op == NodeType::FUNC);

        let mut args = Vec::new();
        for a in node.borrow_mut().args.iter_mut() {
            args.push(walk(&mut a.clone(), prog).clone());
        }
        node.borrow_mut().args = args;

        let mut body = node.borrow_mut().body.clone().unwrap();
        node.borrow_mut().body = Some(Box::new(walk(&mut body, prog).clone()));

        let mut off = 0;
        for v in func.lvars.iter_mut() {
            off = roundup(off, v.borrow().ty.align);
            off += v.borrow().ty.size;
            v.borrow_mut().offset = off;
        }
        func.stacksize = off;
    }

    prog.funcs = funcs;
}
<|endoftext|>


<commit_before>extern crate ncurses;
use ncurses::*;
use Cursorstate;
use std::cmp;

pub fn draw(
    buf: &[u8],
    cursorpos: usize,
    cols: usize,
    command: &String,
    debug: &mut String,
    cstate: Cursorstate,
    screenoffset: usize,
) {
    erase();

//    debug.clear();
//    let screensize = get_screen_size(cols);
//    debug.push_str(&format!("   screensize:{:?}", screensize));
//    debug.push_str(&format!("   screenoffset:{:?}", screenoffset));
//    debug.push_str(&format!("   buf.len():{:?}", buf.len()));

    let screenheight: usize;
    screenheight = getmaxy(stdscr()) as usize;
//    debug.push_str(&format!("   screenheight:{:?}", screenheight));

    let mut tmpbuflen = buf.len();
    if tmpbuflen >= 1 { tmpbuflen -= 1; }
    let rows = tmpbuflen / cols + 1;

//    debug.push_str(&format!("   rows:{:?}", rows));

    for z in 0..rows {
        // 8 hex digits (4GB/cols or 0.25GB@cols=SPALTEN)
        addstr(&format!("{:08X}: ", get_absolute_line(cols, screenoffset, z)));
        // Additional space between line number and hex
        addstr(" ");
        for s in 0..cols {
            let pos: usize = z*cols + s;
            if pos < buf.len() {

                color_left_nibble_cond(true, pos+cols*screenoffset == cursorpos, cstate);
                addstr(&format!("{:01X}", buf[pos] >> 4));
                color_left_nibble_cond(false, pos+cols*screenoffset == cursorpos, cstate);

                color_right_nibble_cond(true, pos+cols*screenoffset == cursorpos, cstate);
                addstr(&format!("{:01X}", buf[pos] & 0x0F));
                color_right_nibble_cond(false, pos+cols*screenoffset == cursorpos, cstate);

                addstr(" ");
            } else if pos == buf.len() {

                color_left_nibble_cond(true, pos+cols*screenoffset == cursorpos, cstate);
                addstr("-");
                color_left_nibble_cond(false, pos+cols*screenoffset == cursorpos, cstate);

                color_right_nibble_cond(true, pos+cols*screenoffset == cursorpos, cstate);
                addstr("-");
                color_right_nibble_cond(false, pos+cols*screenoffset == cursorpos, cstate);

                addstr(" ");
            } else {
                addstr("-- ");
            }
        }
        // Additional space between hex and ascii
        addstr(" ");
        for s in 0..cols {
            let pos: usize = z*cols + s;
            color_ascii_cond(true, pos+cols*screenoffset == cursorpos, cstate);
            if pos < buf.len() {
                if let c @ 32...126 = buf[pos] {
                    if c as char == '%' {
                        // '%' needs to be escaped by a '%' in ncurses
                        addstr("%%");
                    } else {
                        addstr(&format!("{}", c as char));
                    }
                } else {
                    // Mark non-ascii symbols
                    addstr(&format!("."));
                }
            } else if pos == buf.len() {
                // Pad ascii with spaces
                addstr(" ");
            }

            color_ascii_cond(false, pos+cols*screenoffset == cursorpos, cstate);
        }
        addstr("\n");
    }
    for _ in 1 .. screenheight - rows {
        // Put the cursor on last line of terminal
        addstr("\n");
    }
    addstr(&format!("{}", command));
    addstr(&format!("{}", debug));
}

fn get_absolute_line(cols: usize, screenoffset: usize, z: usize) -> usize {
    return z * cols + screenoffset * cols;
}
pub fn get_screen_size(
    cols: usize,
    ) -> usize {
    let screenheight: usize;
    screenheight = getmaxy(stdscr()) as usize;

    let mut ret: usize = 0;

    // Last line reserved for Status/Commands/etc (Like in vim)
    if screenheight >= 1 {
        ret = (screenheight-1) * cols;
    }
    return ret;
}
pub fn get_absolute_draw_indices(
    buflen: usize,
    cols: usize,
    screenoffset: usize,
    ) -> (usize, usize) {

    let max_draw_len:usize = cmp::min(buflen, get_screen_size(cols)); //hier muss bei get_screen_size noch auf 16er abgerundet werden?

    let starting_pos: usize = screenoffset * cols;
    let mut ending_pos: usize = starting_pos + max_draw_len;
    if ending_pos > buflen {
        ending_pos = buflen;
    }

    return (starting_pos, ending_pos);
}

fn color_left_nibble(color: bool, cstate: Cursorstate) {
    if color {
        if cstate == Cursorstate::Leftnibble {
            attron(COLOR_PAIR(1) | A_STANDOUT());
        } else if cstate == Cursorstate::Asciichar {
            attron(A_UNDERLINE());
        }
    } else {
        if cstate == Cursorstate::Leftnibble {
            attroff(COLOR_PAIR(1) | A_STANDOUT());
        } else if cstate == Cursorstate::Asciichar {
            attroff(A_UNDERLINE());
        }
    }
}
fn color_left_nibble_cond(color: bool, condition: bool, cstate: Cursorstate) {
    if condition {
        color_left_nibble(color, cstate);
    }
}

fn color_right_nibble(color: bool, cstate: Cursorstate) {
    if color {
        if cstate == Cursorstate::Rightnibble {
            attron(COLOR_PAIR(1) | A_STANDOUT());
        } else if cstate == Cursorstate::Asciichar {
            attron(A_UNDERLINE());
        }
    } else {
        if cstate == Cursorstate::Rightnibble {
            attroff(COLOR_PAIR(1) | A_STANDOUT());
        } else if cstate == Cursorstate::Asciichar {
            attroff(A_UNDERLINE());
        }
    }
}
fn color_right_nibble_cond(color: bool, condition: bool, cstate: Cursorstate) {
    if condition {
        color_right_nibble(color, cstate);
    }
}

fn color_ascii(color: bool, cstate: Cursorstate) {
    if color {
        if cstate == Cursorstate::Asciichar {
            attron(COLOR_PAIR(1) | A_STANDOUT());
        } else {
            attron(A_UNDERLINE());
        }
    } else {
        if cstate == Cursorstate::Asciichar {
            attroff(COLOR_PAIR(1) | A_STANDOUT());
        } else {
            attroff(A_UNDERLINE());
        }
    }
}
fn color_ascii_cond(color: bool, condition: bool, cstate: Cursorstate) {
    if condition {
        color_ascii(color, cstate);
    }
}
<commit_msg>Remove '%'-escape from old ncurses::printw<commit_after>extern crate ncurses;
use ncurses::*;
use Cursorstate;
use std::cmp;

pub fn draw(
    buf: &[u8],
    cursorpos: usize,
    cols: usize,
    command: &String,
    debug: &mut String,
    cstate: Cursorstate,
    screenoffset: usize,
) {
    erase();

//    debug.clear();
//    let screensize = get_screen_size(cols);
//    debug.push_str(&format!("   screensize:{:?}", screensize));
//    debug.push_str(&format!("   screenoffset:{:?}", screenoffset));
//    debug.push_str(&format!("   buf.len():{:?}", buf.len()));

    let screenheight: usize;
    screenheight = getmaxy(stdscr()) as usize;
//    debug.push_str(&format!("   screenheight:{:?}", screenheight));

    let mut tmpbuflen = buf.len();
    if tmpbuflen >= 1 { tmpbuflen -= 1; }
    let rows = tmpbuflen / cols + 1;

//    debug.push_str(&format!("   rows:{:?}", rows));

    for z in 0..rows {
        // 8 hex digits (4GB/cols or 0.25GB@cols=SPALTEN)
        addstr(&format!("{:08X}: ", get_absolute_line(cols, screenoffset, z)));
        // Additional space between line number and hex
        addstr(" ");
        for s in 0..cols {
            let pos: usize = z*cols + s;
            if pos < buf.len() {

                color_left_nibble_cond(true, pos+cols*screenoffset == cursorpos, cstate);
                addstr(&format!("{:01X}", buf[pos] >> 4));
                color_left_nibble_cond(false, pos+cols*screenoffset == cursorpos, cstate);

                color_right_nibble_cond(true, pos+cols*screenoffset == cursorpos, cstate);
                addstr(&format!("{:01X}", buf[pos] & 0x0F));
                color_right_nibble_cond(false, pos+cols*screenoffset == cursorpos, cstate);

                addstr(" ");
            } else if pos == buf.len() {

                color_left_nibble_cond(true, pos+cols*screenoffset == cursorpos, cstate);
                addstr("-");
                color_left_nibble_cond(false, pos+cols*screenoffset == cursorpos, cstate);

                color_right_nibble_cond(true, pos+cols*screenoffset == cursorpos, cstate);
                addstr("-");
                color_right_nibble_cond(false, pos+cols*screenoffset == cursorpos, cstate);

                addstr(" ");
            } else {
                addstr("-- ");
            }
        }
        // Additional space between hex and ascii
        addstr(" ");
        for s in 0..cols {
            let pos: usize = z*cols + s;
            color_ascii_cond(true, pos+cols*screenoffset == cursorpos, cstate);
            if pos < buf.len() {
                if let c @ 32...126 = buf[pos] {
                    addstr(&format!("{}", c as char));
                } else {
                    // Mark non-ascii symbols
                    addstr(&format!("."));
                }
            } else if pos == buf.len() {
                // Pad ascii with spaces
                addstr(" ");
            }

            color_ascii_cond(false, pos+cols*screenoffset == cursorpos, cstate);
        }
        addstr("\n");
    }
    for _ in 1 .. screenheight - rows {
        // Put the cursor on last line of terminal
        addstr("\n");
    }
    addstr(&format!("{}", command));
    addstr(&format!("{}", debug));
}

fn get_absolute_line(cols: usize, screenoffset: usize, z: usize) -> usize {
    return z * cols + screenoffset * cols;
}
pub fn get_screen_size(
    cols: usize,
    ) -> usize {
    let screenheight: usize;
    screenheight = getmaxy(stdscr()) as usize;

    let mut ret: usize = 0;

    // Last line reserved for Status/Commands/etc (Like in vim)
    if screenheight >= 1 {
        ret = (screenheight-1) * cols;
    }
    return ret;
}
pub fn get_absolute_draw_indices(
    buflen: usize,
    cols: usize,
    screenoffset: usize,
    ) -> (usize, usize) {

    let max_draw_len:usize = cmp::min(buflen, get_screen_size(cols)); //hier muss bei get_screen_size noch auf 16er abgerundet werden?

    let starting_pos: usize = screenoffset * cols;
    let mut ending_pos: usize = starting_pos + max_draw_len;
    if ending_pos > buflen {
        ending_pos = buflen;
    }

    return (starting_pos, ending_pos);
}

fn color_left_nibble(color: bool, cstate: Cursorstate) {
    if color {
        if cstate == Cursorstate::Leftnibble {
            attron(COLOR_PAIR(1) | A_STANDOUT());
        } else if cstate == Cursorstate::Asciichar {
            attron(A_UNDERLINE());
        }
    } else {
        if cstate == Cursorstate::Leftnibble {
            attroff(COLOR_PAIR(1) | A_STANDOUT());
        } else if cstate == Cursorstate::Asciichar {
            attroff(A_UNDERLINE());
        }
    }
}
fn color_left_nibble_cond(color: bool, condition: bool, cstate: Cursorstate) {
    if condition {
        color_left_nibble(color, cstate);
    }
}

fn color_right_nibble(color: bool, cstate: Cursorstate) {
    if color {
        if cstate == Cursorstate::Rightnibble {
            attron(COLOR_PAIR(1) | A_STANDOUT());
        } else if cstate == Cursorstate::Asciichar {
            attron(A_UNDERLINE());
        }
    } else {
        if cstate == Cursorstate::Rightnibble {
            attroff(COLOR_PAIR(1) | A_STANDOUT());
        } else if cstate == Cursorstate::Asciichar {
            attroff(A_UNDERLINE());
        }
    }
}
fn color_right_nibble_cond(color: bool, condition: bool, cstate: Cursorstate) {
    if condition {
        color_right_nibble(color, cstate);
    }
}

fn color_ascii(color: bool, cstate: Cursorstate) {
    if color {
        if cstate == Cursorstate::Asciichar {
            attron(COLOR_PAIR(1) | A_STANDOUT());
        } else {
            attron(A_UNDERLINE());
        }
    } else {
        if cstate == Cursorstate::Asciichar {
            attroff(COLOR_PAIR(1) | A_STANDOUT());
        } else {
            attroff(A_UNDERLINE());
        }
    }
}
fn color_ascii_cond(color: bool, condition: bool, cstate: Cursorstate) {
    if condition {
        color_ascii(color, cstate);
    }
}
<|endoftext|>


<commit_before>//! Mach-O Dsym Support
//!
//! This module adds support for reading macho files and to extract some
//! limited set of debug symbols from it.  This is exclusively used for
//! system symbols from iOS SDKs and similar where actual DWARF info is
//! not contained, just symbol tabs.
use std::io::Cursor;
use std::path::Path;
use std::borrow::Cow;

use memmap;
use uuid::Uuid;
use mach_object::{OFile, Symbol, Section, SymbolIter, SymbolReader, DyLib,
    LoadCommand, MachCommand, get_arch_name_from_types, get_arch_from_flag,
    SEG_TEXT, SECT_TEXT, cpu_type_t, cpu_subtype_t};

use super::{Result, Error, ErrorKind};

enum Backing<'a> {
    Buf(Cow<'a, [u8]>),
    Mmap(memmap::Mmap),
}

/// Mach-O objects
///
/// This represents mach objects from either a mmaped file or an in-memory
/// byte slice.
pub struct Object<'a> {
    backing: Backing<'a>,
    ofile: OFile,
    variants: Vec<Variant>,
}

/// Represents an iterator over symbols
pub struct SymbolIterator<'a> {
    iter: Option<SymbolIter<'a>>,
}

/// Provides access to symbols in an object
pub struct Symbols<'a> {
    cputype: cpu_type_t,
    cpusubtype: cpu_subtype_t,
    ofile: &'a OFile,
    cursor: Cursor<&'a [u8]>,
}

/// Represents a variant in an object
pub struct Variant {
    cputype: cpu_type_t,
    cpusubtype: cpu_subtype_t,
    uuid: Option<Uuid>,
    name: Option<String>,
    vmaddr: u64,
    vmsize: u64,
}

impl<'a> Symbols<'a> {
    /// Returns the architecture for these symbols
    pub fn arch(&self) -> &str {
        get_arch_name_from_types(self.cputype, self.cpusubtype).unwrap_or("unknown")
    }

    /// Returns an iterator over contained symbols.
    pub fn iter(&'a mut self) -> SymbolIterator<'a> {
        SymbolIterator {
            iter: self.ofile.symbols(&mut self.cursor),
        }
    }
}

impl<'a> Iterator for SymbolIterator<'a> {
    type Item = (u64, &'a str);

    fn next(&mut self) -> Option<(u64, &'a str)> {
        let iter = try_opt!(self.iter.as_mut());
        while let Some(sym) = iter.next() {
            if let Symbol::Defined { ref name, external, ref section, entry, .. } = sym {
                if !external && name.is_some() {
                    if let &Some(ref sect) = section {
                        let Section { ref sectname, ref segname, .. } = **sect;
                        if segname == SEG_TEXT && sectname == SECT_TEXT {
                            return Some((entry as u64, name.unwrap()));
                        }
                    }
                }
            }
        }
        None
    }
}

impl<'a> Backing<'a> {

    #[inline(always)]
    pub fn buffer(&self) -> &[u8] {
        match *self {
            Backing::Buf(ref buf) => buf,
            Backing::Mmap(ref mmap) => unsafe { mmap.as_slice() }
        }
    }

    #[inline(always)]
    pub fn cursor(&self, offset: usize) -> Cursor<&[u8]> {
        Cursor::new(&self.buffer()[offset..])
    }
}

fn extract_variant<'a>(variants: &'a mut Vec<Variant>, file: &'a OFile) {
    if let &OFile::MachFile { ref header, ref commands, .. } = file {
        let mut variant_uuid = None;
        let mut variant_name = None;
        let mut variant_vmaddr = 0;
        let mut variant_vmsize = 0;
        for &MachCommand(ref load_cmd, _) in commands {
            match load_cmd {
                &LoadCommand::Uuid(uuid) => {
                    variant_uuid = Some(uuid);
                },
                &LoadCommand::IdDyLib(DyLib { ref name, .. }) => {
                    variant_name = Some(name.1.clone());
                }
                &LoadCommand::Segment { ref segname, vmaddr, vmsize, .. } => {
                    if segname == "__TEXT" {
                        variant_vmaddr = vmaddr as u64;
                        variant_vmsize = vmsize as u64;
                    }
                }
                &LoadCommand::Segment64 { ref segname, vmaddr, vmsize, .. } => {
                    if segname == "__TEXT" {
                        variant_vmaddr = vmaddr as u64;
                        variant_vmsize = vmsize as u64;
                    }
                }
                _ => {}
            }
        }
        variants.push(Variant {
            cputype: header.cputype,
            cpusubtype: header.cpusubtype,
            uuid: variant_uuid,
            name: variant_name,
            vmaddr: variant_vmaddr,
            vmsize: variant_vmsize,
        })
    }
}

impl<'a> Object<'a> {

    fn from_backing(backing: Backing<'a>) -> Result<Object<'a>> {
        let ofile = OFile::parse(&mut backing.cursor(0))?;
        let mut variants = vec![];

        match ofile {
            OFile::FatFile { ref files, .. } => {
                for &(_, ref file) in files {
                    extract_variant(&mut variants, file);
                }
            }
            OFile::MachFile { .. } => {
                extract_variant(&mut variants, &ofile);
            }
            _ => {}
        }

        Ok(Object {
            backing: backing,
            ofile: ofile,
            variants: variants,
        })
    }

    fn from_cow(cow: Cow<'a, [u8]>) -> Result<Object<'a>> {
        Object::from_backing(Backing::Buf(cow))
    }

    /// Parses a macho object from a given slice.
    pub fn from_slice(buf: &'a [u8]) -> Result<Object<'a>> {
        Object::from_cow(Cow::Borrowed(buf))
    }

    /// Parses a macho object from a vector.
    pub fn from_vec(buf: Vec<u8>) -> Result<Object<'a>> {
        Object::from_cow(Cow::Owned(buf))
    }

    /// Parses a macho object from a memory mapped file.
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Object<'a>> {
        let mmap = memmap::Mmap::open_path(path, memmap::Protection::Read)?;
        Object::from_backing(Backing::Mmap(mmap))
    }

    /// Return a slice of the variants
    pub fn variants(&'a self) -> &'a [Variant] {
        &self.variants[..]
    }

    /// Returns an object providing symbol access for a given
    /// architecture.
    ///
    /// The architecture can be found from the provided variant.
    pub fn symbols<'b>(&'a self, arch: &'b str) -> Result<Symbols<'a>> {
        let &(cputype, cpusubtype) = get_arch_from_flag(arch).ok_or_else(|| {
            Error::from(ErrorKind::UnknownArchitecture(arch.to_string()))
        })?;

        match self.ofile {
            OFile::FatFile { ref files, .. } => {
                for &(ref arch, ref file) in files {
                    if arch.cputype == cputype && arch.cpusubtype == cpusubtype {
                        return Ok(Symbols {
                            cputype: arch.cputype,
                            cpusubtype: arch.cpusubtype,
                            ofile: file,
                            cursor: self.backing.cursor(arch.offset as usize),
                        });
                    }
                }
            }
            OFile::MachFile { ref header, .. } => {
                if header.cputype == cputype && header.cpusubtype == cpusubtype {
                    return Ok(Symbols {
                        cputype: header.cputype,
                        cpusubtype: header.cpusubtype,
                        ofile: &self.ofile,
                        cursor: self.backing.cursor(0),
                    });
                }
            }
            _ => {}
        }

        return Err(ErrorKind::MissingArchitecture(arch.to_string()).into());
    }
}

impl Variant {
    /// Returns the architecture of this variant
    pub fn arch(&self) -> &str {
        get_arch_name_from_types(self.cputype, self.cpusubtype).unwrap_or("unknown")
    }

    /// Returns the contained name of the variant
    ///
    /// This is the full path of the debug symbol / object file if stored.
    pub fn name(&self) -> Option<&str> {
        self.name.as_ref().map(|x| x.as_str())
    }

    /// Returns the UUID of the variant
    ///
    /// Normally mach-o files have contained UUIDs.  In case we have one, it's
    /// being returned here.
    pub fn uuid(&self) -> Option<Uuid> {
        self.uuid
    }

    /// The vmaddr as u64. Might be 0
    pub fn vmaddr(&self) -> u64 {
        self.vmaddr
    }

    /// The vmsize as u64. Might be 0
    pub fn vmsize(&self) -> u64 {
        self.vmsize
    }
}
<commit_msg>Remove external check<commit_after>//! Mach-O Dsym Support
//!
//! This module adds support for reading macho files and to extract some
//! limited set of debug symbols from it.  This is exclusively used for
//! system symbols from iOS SDKs and similar where actual DWARF info is
//! not contained, just symbol tabs.
use std::io::Cursor;
use std::path::Path;
use std::borrow::Cow;

use memmap;
use uuid::Uuid;
use mach_object::{OFile, Symbol, Section, SymbolIter, SymbolReader, DyLib,
    LoadCommand, MachCommand, get_arch_name_from_types, get_arch_from_flag,
    SEG_TEXT, SECT_TEXT, cpu_type_t, cpu_subtype_t};

use super::{Result, Error, ErrorKind};

enum Backing<'a> {
    Buf(Cow<'a, [u8]>),
    Mmap(memmap::Mmap),
}

/// Mach-O objects
///
/// This represents mach objects from either a mmaped file or an in-memory
/// byte slice.
pub struct Object<'a> {
    backing: Backing<'a>,
    ofile: OFile,
    variants: Vec<Variant>,
}

/// Represents an iterator over symbols
pub struct SymbolIterator<'a> {
    iter: Option<SymbolIter<'a>>,
}

/// Provides access to symbols in an object
pub struct Symbols<'a> {
    cputype: cpu_type_t,
    cpusubtype: cpu_subtype_t,
    ofile: &'a OFile,
    cursor: Cursor<&'a [u8]>,
}

/// Represents a variant in an object
pub struct Variant {
    cputype: cpu_type_t,
    cpusubtype: cpu_subtype_t,
    uuid: Option<Uuid>,
    name: Option<String>,
    vmaddr: u64,
    vmsize: u64,
}

impl<'a> Symbols<'a> {
    /// Returns the architecture for these symbols
    pub fn arch(&self) -> &str {
        get_arch_name_from_types(self.cputype, self.cpusubtype).unwrap_or("unknown")
    }

    /// Returns an iterator over contained symbols.
    pub fn iter(&'a mut self) -> SymbolIterator<'a> {
        SymbolIterator {
            iter: self.ofile.symbols(&mut self.cursor),
        }
    }
}

impl<'a> Iterator for SymbolIterator<'a> {
    type Item = (u64, &'a str);

    fn next(&mut self) -> Option<(u64, &'a str)> {
        let iter = try_opt!(self.iter.as_mut());
        while let Some(sym) = iter.next() {
            if let Symbol::Defined { ref name, ref section, entry, .. } = sym {
                if name.is_some() {
                    if let &Some(ref sect) = section {
                        let Section { ref sectname, ref segname, .. } = **sect;
                        if segname == SEG_TEXT && sectname == SECT_TEXT {
                            return Some((entry as u64, name.unwrap()));
                        }
                    }
                }
            }
        }
        None
    }
}

impl<'a> Backing<'a> {

    #[inline(always)]
    pub fn buffer(&self) -> &[u8] {
        match *self {
            Backing::Buf(ref buf) => buf,
            Backing::Mmap(ref mmap) => unsafe { mmap.as_slice() }
        }
    }

    #[inline(always)]
    pub fn cursor(&self, offset: usize) -> Cursor<&[u8]> {
        Cursor::new(&self.buffer()[offset..])
    }
}

fn extract_variant<'a>(variants: &'a mut Vec<Variant>, file: &'a OFile) {
    if let &OFile::MachFile { ref header, ref commands, .. } = file {
        let mut variant_uuid = None;
        let mut variant_name = None;
        let mut variant_vmaddr = 0;
        let mut variant_vmsize = 0;
        for &MachCommand(ref load_cmd, _) in commands {
            match load_cmd {
                &LoadCommand::Uuid(uuid) => {
                    variant_uuid = Some(uuid);
                },
                &LoadCommand::IdDyLib(DyLib { ref name, .. }) => {
                    variant_name = Some(name.1.clone());
                }
                &LoadCommand::Segment { ref segname, vmaddr, vmsize, .. } => {
                    if segname == "__TEXT" {
                        variant_vmaddr = vmaddr as u64;
                        variant_vmsize = vmsize as u64;
                    }
                }
                &LoadCommand::Segment64 { ref segname, vmaddr, vmsize, .. } => {
                    if segname == "__TEXT" {
                        variant_vmaddr = vmaddr as u64;
                        variant_vmsize = vmsize as u64;
                    }
                }
                _ => {}
            }
        }
        variants.push(Variant {
            cputype: header.cputype,
            cpusubtype: header.cpusubtype,
            uuid: variant_uuid,
            name: variant_name,
            vmaddr: variant_vmaddr,
            vmsize: variant_vmsize,
        })
    }
}

impl<'a> Object<'a> {

    fn from_backing(backing: Backing<'a>) -> Result<Object<'a>> {
        let ofile = OFile::parse(&mut backing.cursor(0))?;
        let mut variants = vec![];

        match ofile {
            OFile::FatFile { ref files, .. } => {
                for &(_, ref file) in files {
                    extract_variant(&mut variants, file);
                }
            }
            OFile::MachFile { .. } => {
                extract_variant(&mut variants, &ofile);
            }
            _ => {}
        }

        Ok(Object {
            backing: backing,
            ofile: ofile,
            variants: variants,
        })
    }

    fn from_cow(cow: Cow<'a, [u8]>) -> Result<Object<'a>> {
        Object::from_backing(Backing::Buf(cow))
    }

    /// Parses a macho object from a given slice.
    pub fn from_slice(buf: &'a [u8]) -> Result<Object<'a>> {
        Object::from_cow(Cow::Borrowed(buf))
    }

    /// Parses a macho object from a vector.
    pub fn from_vec(buf: Vec<u8>) -> Result<Object<'a>> {
        Object::from_cow(Cow::Owned(buf))
    }

    /// Parses a macho object from a memory mapped file.
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Object<'a>> {
        let mmap = memmap::Mmap::open_path(path, memmap::Protection::Read)?;
        Object::from_backing(Backing::Mmap(mmap))
    }

    /// Return a slice of the variants
    pub fn variants(&'a self) -> &'a [Variant] {
        &self.variants[..]
    }

    /// Returns an object providing symbol access for a given
    /// architecture.
    ///
    /// The architecture can be found from the provided variant.
    pub fn symbols<'b>(&'a self, arch: &'b str) -> Result<Symbols<'a>> {
        let &(cputype, cpusubtype) = get_arch_from_flag(arch).ok_or_else(|| {
            Error::from(ErrorKind::UnknownArchitecture(arch.to_string()))
        })?;

        match self.ofile {
            OFile::FatFile { ref files, .. } => {
                for &(ref arch, ref file) in files {
                    if arch.cputype == cputype && arch.cpusubtype == cpusubtype {
                        return Ok(Symbols {
                            cputype: arch.cputype,
                            cpusubtype: arch.cpusubtype,
                            ofile: file,
                            cursor: self.backing.cursor(arch.offset as usize),
                        });
                    }
                }
            }
            OFile::MachFile { ref header, .. } => {
                if header.cputype == cputype && header.cpusubtype == cpusubtype {
                    return Ok(Symbols {
                        cputype: header.cputype,
                        cpusubtype: header.cpusubtype,
                        ofile: &self.ofile,
                        cursor: self.backing.cursor(0),
                    });
                }
            }
            _ => {}
        }

        return Err(ErrorKind::MissingArchitecture(arch.to_string()).into());
    }
}

impl Variant {
    /// Returns the architecture of this variant
    pub fn arch(&self) -> &str {
        get_arch_name_from_types(self.cputype, self.cpusubtype).unwrap_or("unknown")
    }

    /// Returns the contained name of the variant
    ///
    /// This is the full path of the debug symbol / object file if stored.
    pub fn name(&self) -> Option<&str> {
        self.name.as_ref().map(|x| x.as_str())
    }

    /// Returns the UUID of the variant
    ///
    /// Normally mach-o files have contained UUIDs.  In case we have one, it's
    /// being returned here.
    pub fn uuid(&self) -> Option<Uuid> {
        self.uuid
    }

    /// The vmaddr as u64. Might be 0
    pub fn vmaddr(&self) -> u64 {
        self.vmaddr
    }

    /// The vmsize as u64. Might be 0
    pub fn vmsize(&self) -> u64 {
        self.vmsize
    }
}
<|endoftext|>


<commit_before>use std::error::Error as StdError;
use std::fmt::{Display, Formatter, Result as FmtResult};
use super::Permissions;

/// An error returned from the [`model`] module.
///
/// This is always wrapped within the library's [`Error::Model`] variant.
///
/// # Examples
///
/// Matching an [`Error`] with this variant would look something like the
/// following for the [`GuildId::ban`] method, which in this example is used to
/// re-ban all members with an odd discriminator:
///
/// ```rust,no_run
/// # #[cfg(feature="client")]
/// # use std::error::Error;
/// #
/// # #[cfg(feature="client")]
/// # fn try_main() -> Result<(), Box<Error>> {
/// use serenity::{Client, Error};
/// use serenity::model::ModelError;
/// use std::env;
///
/// let token = env::var("DISCORD_BOT_TOKEN")?;
/// let mut client = Client::new(&token);
///
/// client.on_member_unban(|context, guild_id, user| {
///     // If the user has an even discriminator, don't re-ban them.
///     if user.discriminator % 2 == 0 {
///         return;
///     }
///
///     match guild_id.ban(user, 8) {
///         Ok(()) => {
///             // Ban successful.
///         },
///         Err(Error::Model(ModelError::DeleteMessageDaysAmount(amount))) => {
///             println!("Failed deleting {} days' worth of messages", amount);
///         },
///         Err(why) => {
///             println!("Unexpected error: {:?}", why);
///         },
///     }
/// });
/// #     Ok(())
/// # }
/// #
/// # #[cfg(feature="client")]
/// # fn main() {
/// #     try_main().unwrap();
/// # }
/// #
/// # #[cfg(not(feature="client"))]
/// # fn main() { }
/// ```
///
/// [`Error`]: ../enum.Error.html
/// [`Error::Model`]: ../enum.Error.html#variant.Model
/// [`GuildId::ban`]: struct.GuildId.html#method.ban
/// [`model`]: ./index.html
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Error {
    /// When attempting to delete below or above the minimum and maximum allowed
    /// number of messages.
    BulkDeleteAmount,
    /// When attempting to delete a number of days' worth of messages that is
    /// not allowed.
    DeleteMessageDaysAmount(u8),
    /// Indicates that the textual content of an embed exceeds the maximum
    /// length.
    EmbedTooLarge(u64),
    /// An indication that a [guild][`Guild`] could not be found by
    /// [Id][`GuildId`] in the [`Cache`].
    ///
    /// [`Guild`]: ../model/struct.Guild.html
    /// [`GuildId`]: ../model/struct.GuildId.html
    /// [`Cache`]: ../cache/struct.Cache.html
    GuildNotFound,
    /// Indicates that you do not have the required permissions to perform an
    /// operation.
    ///
    /// The provided [`Permission`]s is the set of required permissions
    /// required.
    ///
    /// [`Permission`]: ../model/permissions/struct.Permissions.html
    InvalidPermissions(Permissions),
    /// An indicator that the [current user] can not perform an action.
    ///
    /// [current user]: ../model/struct.CurrentUser.html
    InvalidUser,
    /// An indicator that an item is missing from the [`Cache`], and the action
    /// can not be continued.
    ///
    /// [`Cache`]: ../cache/struct.Cache.html
    ItemMissing,
    /// Indicates that a [`Message`]s content was too long and will not
    /// successfully send, as the length is over 2000 codepoints, or 4000 bytes.
    ///
    /// The number of bytes larger than the limit is provided.
    ///
    /// [`Message`]: ../model/struct.Message.html
    MessageTooLong(u64),
    /// Indicates that the current user is attempting to Direct Message another
    /// bot user, which is disallowed by the API.
    MessagingBot,
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        f.write_str(self.description())
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::BulkDeleteAmount => "Too few/many messages to bulk delete",
            Error::DeleteMessageDaysAmount(_) => "Invalid delete message days",
            Error::EmbedTooLarge(_) => "Embed too large",
            Error::GuildNotFound => "Guild not found in the cache",
            Error::InvalidPermissions(_) => "Invalid permissions",
            Error::InvalidUser => "The current user can not perform the action",
            Error::ItemMissing => "The required item is missing from the cache",
            Error::MessageTooLong(_) => "Message too large",
        }
    }
}
<commit_msg>Add missing ModelError variant in description impl<commit_after>use std::error::Error as StdError;
use std::fmt::{Display, Formatter, Result as FmtResult};
use super::Permissions;

/// An error returned from the [`model`] module.
///
/// This is always wrapped within the library's [`Error::Model`] variant.
///
/// # Examples
///
/// Matching an [`Error`] with this variant would look something like the
/// following for the [`GuildId::ban`] method, which in this example is used to
/// re-ban all members with an odd discriminator:
///
/// ```rust,no_run
/// # #[cfg(feature="client")]
/// # use std::error::Error;
/// #
/// # #[cfg(feature="client")]
/// # fn try_main() -> Result<(), Box<Error>> {
/// use serenity::{Client, Error};
/// use serenity::model::ModelError;
/// use std::env;
///
/// let token = env::var("DISCORD_BOT_TOKEN")?;
/// let mut client = Client::new(&token);
///
/// client.on_member_unban(|context, guild_id, user| {
///     // If the user has an even discriminator, don't re-ban them.
///     if user.discriminator % 2 == 0 {
///         return;
///     }
///
///     match guild_id.ban(user, 8) {
///         Ok(()) => {
///             // Ban successful.
///         },
///         Err(Error::Model(ModelError::DeleteMessageDaysAmount(amount))) => {
///             println!("Failed deleting {} days' worth of messages", amount);
///         },
///         Err(why) => {
///             println!("Unexpected error: {:?}", why);
///         },
///     }
/// });
/// #     Ok(())
/// # }
/// #
/// # #[cfg(feature="client")]
/// # fn main() {
/// #     try_main().unwrap();
/// # }
/// #
/// # #[cfg(not(feature="client"))]
/// # fn main() { }
/// ```
///
/// [`Error`]: ../enum.Error.html
/// [`Error::Model`]: ../enum.Error.html#variant.Model
/// [`GuildId::ban`]: struct.GuildId.html#method.ban
/// [`model`]: ./index.html
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Error {
    /// When attempting to delete below or above the minimum and maximum allowed
    /// number of messages.
    BulkDeleteAmount,
    /// When attempting to delete a number of days' worth of messages that is
    /// not allowed.
    DeleteMessageDaysAmount(u8),
    /// Indicates that the textual content of an embed exceeds the maximum
    /// length.
    EmbedTooLarge(u64),
    /// An indication that a [guild][`Guild`] could not be found by
    /// [Id][`GuildId`] in the [`Cache`].
    ///
    /// [`Guild`]: ../model/struct.Guild.html
    /// [`GuildId`]: ../model/struct.GuildId.html
    /// [`Cache`]: ../cache/struct.Cache.html
    GuildNotFound,
    /// Indicates that you do not have the required permissions to perform an
    /// operation.
    ///
    /// The provided [`Permission`]s is the set of required permissions
    /// required.
    ///
    /// [`Permission`]: ../model/permissions/struct.Permissions.html
    InvalidPermissions(Permissions),
    /// An indicator that the [current user] can not perform an action.
    ///
    /// [current user]: ../model/struct.CurrentUser.html
    InvalidUser,
    /// An indicator that an item is missing from the [`Cache`], and the action
    /// can not be continued.
    ///
    /// [`Cache`]: ../cache/struct.Cache.html
    ItemMissing,
    /// Indicates that a [`Message`]s content was too long and will not
    /// successfully send, as the length is over 2000 codepoints, or 4000 bytes.
    ///
    /// The number of bytes larger than the limit is provided.
    ///
    /// [`Message`]: ../model/struct.Message.html
    MessageTooLong(u64),
    /// Indicates that the current user is attempting to Direct Message another
    /// bot user, which is disallowed by the API.
    MessagingBot,
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        f.write_str(self.description())
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::BulkDeleteAmount => "Too few/many messages to bulk delete",
            Error::DeleteMessageDaysAmount(_) => "Invalid delete message days",
            Error::EmbedTooLarge(_) => "Embed too large",
            Error::GuildNotFound => "Guild not found in the cache",
            Error::InvalidPermissions(_) => "Invalid permissions",
            Error::InvalidUser => "The current user can not perform the action",
            Error::ItemMissing => "The required item is missing from the cache",
            Error::MessageTooLong(_) => "Message too large",
            Error::MessagingBot => "Attempted to message another bot user",
        }
    }
}
<|endoftext|>


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

use std::ffi::OsStr;

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 rustc_mir::interpret::InterpErrorInfo;

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,
    /// Determines if memory leaks should be ignored.
    pub ignore_leaks: 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>,
    /// The stacked borrow id to report about
    pub tracked_pointer_tag: Option<PtrId>,
}

/// Details of premature program termination.
pub enum TerminationInfo {
    Exit(i64),
    PoppedTrackedPointerTag(Item),
    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(rustc_span::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,
            config.tracked_pointer_tag,
        ),
    );
    // 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.arg_count != 0 {
        bug!("main function must not take any arguments");
    }

    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 = {
        // Put each argument in memory, collect pointers.
        let mut argvs = Vec::<Scalar<Tag>>::new();
        for arg in config.args.iter() {
            // Make space for `0` terminator.
            let size = arg.len() as u64 + 1;
            let arg_type = tcx.mk_array(tcx.types.u8, size);
            let arg_place = ecx.allocate(ecx.layout_of(arg_type)?, MiriMemoryKind::Env.into());
            ecx.write_os_str_to_c_str(OsStr::new(arg), arg_place.ptr, size)?;
            argvs.push(arg_place.ptr);
        }
        // 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 3rd argument for main.
        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`.
        {
            // 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());

            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::Env.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::Env.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> {
    // FIXME: We always ignore leaks on some platforms where we do not
    // correctly implement TLS destructors.
    let target_os = tcx.sess.target.target.target_os.to_lowercase();
    let ignore_leaks = config.ignore_leaks || target_os == "windows" || target_os == "macos";

    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) => {
            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(e) => report_err(&ecx, e),
    }
}

fn report_err<'tcx, 'mir>(
    ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
    mut e: InterpErrorInfo<'tcx>,
) -> Option<i64> {
    // 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::PoppedTrackedPointerTag(item) =>
                    format!("popped tracked tag for item {:?}", item),
                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::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;
}
<commit_msg>Trailing return<commit_after>//! Main evaluator loop and setting up the initial stack frame.

use std::ffi::OsStr;

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 rustc_mir::interpret::InterpErrorInfo;

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,
    /// Determines if memory leaks should be ignored.
    pub ignore_leaks: 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>,
    /// The stacked borrow id to report about
    pub tracked_pointer_tag: Option<PtrId>,
}

/// Details of premature program termination.
pub enum TerminationInfo {
    Exit(i64),
    PoppedTrackedPointerTag(Item),
    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(rustc_span::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,
            config.tracked_pointer_tag,
        ),
    );
    // 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.arg_count != 0 {
        bug!("main function must not take any arguments");
    }

    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 = {
        // Put each argument in memory, collect pointers.
        let mut argvs = Vec::<Scalar<Tag>>::new();
        for arg in config.args.iter() {
            // Make space for `0` terminator.
            let size = arg.len() as u64 + 1;
            let arg_type = tcx.mk_array(tcx.types.u8, size);
            let arg_place = ecx.allocate(ecx.layout_of(arg_type)?, MiriMemoryKind::Env.into());
            ecx.write_os_str_to_c_str(OsStr::new(arg), arg_place.ptr, size)?;
            argvs.push(arg_place.ptr);
        }
        // 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 3rd argument for main.
        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`.
        {
            // 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());

            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::Env.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::Env.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> {
    // FIXME: We always ignore leaks on some platforms where we do not
    // correctly implement TLS destructors.
    let target_os = tcx.sess.target.target.target_os.to_lowercase();
    let ignore_leaks = config.ignore_leaks || target_os == "windows" || target_os == "macos";

    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) => {
            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;
                }
            }
            Some(return_code)
        }
        Err(e) => report_err(&ecx, e),
    }
}

fn report_err<'tcx, 'mir>(
    ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
    mut e: InterpErrorInfo<'tcx>,
) -> Option<i64> {
    // 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::PoppedTrackedPointerTag(item) =>
                    format!("popped tracked tag for item {:?}", item),
                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::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|>