Reorganize and simplify.
This commit is contained in:
parent
f74d1dc7f1
commit
a7c7764c93
4 changed files with 113 additions and 134 deletions
32
src/error.rs
Normal file
32
src/error.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum EvalError {
|
||||
DanglingPointerDeref,
|
||||
InvalidBool,
|
||||
PointerOutOfBounds,
|
||||
InvalidPointerAccess,
|
||||
}
|
||||
|
||||
pub type EvalResult<T> = Result<T, EvalError>;
|
||||
|
||||
impl Error for EvalError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
EvalError::DanglingPointerDeref => "dangling pointer was dereferenced",
|
||||
EvalError::InvalidBool => "invalid boolean value read",
|
||||
EvalError::PointerOutOfBounds => "pointer offset outside bounds of allocation",
|
||||
EvalError::InvalidPointerAccess =>
|
||||
"a raw memory access tried to access part of a pointer value as bytes",
|
||||
}
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&Error> { None }
|
||||
}
|
||||
|
||||
impl fmt::Display for EvalError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.description())
|
||||
}
|
||||
}
|
|
@ -1,67 +1,20 @@
|
|||
use rustc::middle::const_eval;
|
||||
use rustc::middle::def_id::DefId;
|
||||
use rustc::middle::ty::{self, TyCtxt};
|
||||
use rustc::middle::subst::{Subst, Substs};
|
||||
use rustc::middle::ty::{self, TyCtxt};
|
||||
use rustc::mir::mir_map::MirMap;
|
||||
use rustc::mir::repr as mir;
|
||||
use rustc::util::nodemap::DefIdMap;
|
||||
use std::cell::RefCell;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
use error::EvalResult;
|
||||
use memory::{FieldRepr, Memory, Pointer, Repr};
|
||||
use primval::{self, PrimVal};
|
||||
|
||||
const TRACE_EXECUTION: bool = true;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum EvalError {
|
||||
DanglingPointerDeref,
|
||||
InvalidBool,
|
||||
PointerOutOfBounds,
|
||||
InvalidPointerAccess,
|
||||
}
|
||||
|
||||
pub type EvalResult<T> = Result<T, EvalError>;
|
||||
|
||||
impl Error for EvalError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
EvalError::DanglingPointerDeref => "dangling pointer was dereferenced",
|
||||
EvalError::InvalidBool => "invalid boolean value read",
|
||||
EvalError::PointerOutOfBounds => "pointer offset outside bounds of allocation",
|
||||
EvalError::InvalidPointerAccess =>
|
||||
"a raw memory access tried to access part of a pointer value as bytes",
|
||||
}
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&Error> { None }
|
||||
}
|
||||
|
||||
impl fmt::Display for EvalError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.description())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum CachedMir<'mir, 'tcx: 'mir> {
|
||||
Ref(&'mir mir::Mir<'tcx>),
|
||||
Owned(Rc<mir::Mir<'tcx>>)
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> {
|
||||
type Target = mir::Mir<'tcx>;
|
||||
fn deref(&self) -> &mir::Mir<'tcx> {
|
||||
match *self {
|
||||
CachedMir::Ref(r) => r,
|
||||
CachedMir::Owned(ref rc) => &rc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Interpreter<'a, 'tcx: 'a> {
|
||||
/// The results of the type checker, from rustc.
|
||||
tcx: &'a TyCtxt<'tcx>,
|
||||
|
@ -78,8 +31,8 @@ struct Interpreter<'a, 'tcx: 'a> {
|
|||
/// The virtual call stack.
|
||||
stack: Vec<Frame<'a, 'tcx>>,
|
||||
|
||||
/// Another stack containing the type substitutions for the current function invocation. Exists
|
||||
/// separately from `stack` because it must contain the `Substs` for a function while
|
||||
/// Another stack containing the type substitutions for the current function invocation. It
|
||||
/// exists separately from `stack` because it must contain the `Substs` for a function while
|
||||
/// *creating* the `Frame` for that same function.
|
||||
substs_stack: Vec<&'tcx Substs<'tcx>>,
|
||||
}
|
||||
|
@ -107,6 +60,12 @@ struct Frame<'a, 'tcx: 'a> {
|
|||
temp_offset: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum CachedMir<'mir, 'tcx: 'mir> {
|
||||
Ref(&'mir mir::Mir<'tcx>),
|
||||
Owned(Rc<mir::Mir<'tcx>>)
|
||||
}
|
||||
|
||||
/// Represents the action to be taken in the main loop as a result of executing a terminator.
|
||||
enum TerminatorTarget {
|
||||
/// Make a local jump to the given block.
|
||||
|
@ -131,6 +90,46 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn run(&mut self) -> EvalResult<()> {
|
||||
use std::fmt::Debug;
|
||||
fn print_trace<T: Debug>(t: &T, suffix: &'static str, indent: usize) {
|
||||
if !TRACE_EXECUTION { return; }
|
||||
for _ in 0..indent { print!(" "); }
|
||||
println!("{:?}{}", t, suffix);
|
||||
}
|
||||
|
||||
'outer: while !self.stack.is_empty() {
|
||||
let mut current_block = self.current_frame().next_block;
|
||||
|
||||
loop {
|
||||
print_trace(¤t_block, ":", self.stack.len());
|
||||
let current_mir = self.current_frame().mir.clone(); // Cloning a reference.
|
||||
let block_data = current_mir.basic_block_data(current_block);
|
||||
|
||||
for stmt in &block_data.statements {
|
||||
print_trace(stmt, "", self.stack.len() + 1);
|
||||
let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind;
|
||||
try!(self.eval_assignment(lvalue, rvalue));
|
||||
}
|
||||
|
||||
let terminator = block_data.terminator();
|
||||
print_trace(terminator, "", self.stack.len() + 1);
|
||||
|
||||
match try!(self.eval_terminator(terminator)) {
|
||||
TerminatorTarget::Block(block) => current_block = block,
|
||||
TerminatorTarget::Return => {
|
||||
self.pop_stack_frame();
|
||||
self.substs_stack.pop();
|
||||
continue 'outer;
|
||||
}
|
||||
TerminatorTarget::Call => continue 'outer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn push_stack_frame(&mut self, mir: CachedMir<'a, 'tcx>, args: &[mir::Operand<'tcx>],
|
||||
return_ptr: Option<Pointer>) -> EvalResult<()> {
|
||||
let num_args = mir.arg_decls.len();
|
||||
|
@ -172,74 +171,6 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> {
|
|||
// TODO(tsion): Deallocate local variables.
|
||||
}
|
||||
|
||||
fn load_mir(&self, def_id: DefId) -> CachedMir<'a, 'tcx> {
|
||||
match self.tcx.map.as_local_node_id(def_id) {
|
||||
Some(node_id) => CachedMir::Ref(self.mir_map.map.get(&node_id).unwrap()),
|
||||
None => {
|
||||
let mut mir_cache = self.mir_cache.borrow_mut();
|
||||
if let Some(mir) = mir_cache.get(&def_id) {
|
||||
return CachedMir::Owned(mir.clone());
|
||||
}
|
||||
|
||||
use rustc::middle::cstore::CrateStore;
|
||||
let cs = &self.tcx.sess.cstore;
|
||||
let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap();
|
||||
let cached = Rc::new(mir);
|
||||
mir_cache.insert(def_id, cached.clone());
|
||||
CachedMir::Owned(cached)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run(&mut self) -> EvalResult<()> {
|
||||
fn print_indent(n: usize) {
|
||||
for _ in 0..n {
|
||||
print!(" ");
|
||||
}
|
||||
}
|
||||
|
||||
'outer: while !self.stack.is_empty() {
|
||||
let mut current_block = self.current_frame().next_block;
|
||||
|
||||
loop {
|
||||
if TRACE_EXECUTION {
|
||||
print_indent(self.stack.len());
|
||||
println!("{:?}:", current_block);
|
||||
}
|
||||
|
||||
let current_mir = self.current_frame().mir.clone(); // Cloning a reference.
|
||||
let block_data = current_mir.basic_block_data(current_block);
|
||||
|
||||
for stmt in &block_data.statements {
|
||||
if TRACE_EXECUTION {
|
||||
print_indent(self.stack.len() + 1);
|
||||
println!("{:?}", stmt);
|
||||
}
|
||||
let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind;
|
||||
try!(self.eval_assignment(lvalue, rvalue));
|
||||
}
|
||||
|
||||
let terminator = block_data.terminator();
|
||||
if TRACE_EXECUTION {
|
||||
print_indent(self.stack.len() + 1);
|
||||
println!("{:?}", terminator);
|
||||
}
|
||||
|
||||
match try!(self.eval_terminator(terminator)) {
|
||||
TerminatorTarget::Block(block) => current_block = block,
|
||||
TerminatorTarget::Return => {
|
||||
self.pop_stack_frame();
|
||||
self.substs_stack.pop();
|
||||
continue 'outer;
|
||||
}
|
||||
TerminatorTarget::Call => continue 'outer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<TerminatorTarget> {
|
||||
use rustc::mir::repr::Terminator::*;
|
||||
let target = match *terminator {
|
||||
|
@ -417,9 +348,9 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> {
|
|||
let ptr = match *lvalue {
|
||||
ReturnPointer =>
|
||||
frame.return_ptr.expect("ReturnPointer used in a function with no return value"),
|
||||
Arg(i) => frame.arg_ptr(i),
|
||||
Var(i) => frame.var_ptr(i),
|
||||
Temp(i) => frame.temp_ptr(i),
|
||||
Arg(i) => frame.locals[i as usize],
|
||||
Var(i) => frame.locals[frame.var_offset + i as usize],
|
||||
Temp(i) => frame.locals[frame.temp_offset + i as usize],
|
||||
|
||||
Projection(ref proj) => {
|
||||
let (base_ptr, base_repr) = try!(self.eval_lvalue(&proj.base));
|
||||
|
@ -560,19 +491,34 @@ impl<'a, 'tcx: 'a> Interpreter<'a, 'tcx> {
|
|||
fn current_frame_mut(&mut self) -> &mut Frame<'a, 'tcx> {
|
||||
self.stack.last_mut().expect("no call frames exist")
|
||||
}
|
||||
|
||||
fn load_mir(&self, def_id: DefId) -> CachedMir<'a, 'tcx> {
|
||||
match self.tcx.map.as_local_node_id(def_id) {
|
||||
Some(node_id) => CachedMir::Ref(self.mir_map.map.get(&node_id).unwrap()),
|
||||
None => {
|
||||
let mut mir_cache = self.mir_cache.borrow_mut();
|
||||
if let Some(mir) = mir_cache.get(&def_id) {
|
||||
return CachedMir::Owned(mir.clone());
|
||||
}
|
||||
|
||||
use rustc::middle::cstore::CrateStore;
|
||||
let cs = &self.tcx.sess.cstore;
|
||||
let mir = cs.maybe_get_item_mir(self.tcx, def_id).unwrap();
|
||||
let cached = Rc::new(mir);
|
||||
mir_cache.insert(def_id, cached.clone());
|
||||
CachedMir::Owned(cached)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx: 'a> Frame<'a, 'tcx> {
|
||||
fn arg_ptr(&self, i: u32) -> Pointer {
|
||||
self.locals[i as usize]
|
||||
impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> {
|
||||
type Target = mir::Mir<'tcx>;
|
||||
fn deref(&self) -> &mir::Mir<'tcx> {
|
||||
match *self {
|
||||
CachedMir::Ref(r) => r,
|
||||
CachedMir::Owned(ref rc) => &rc,
|
||||
}
|
||||
|
||||
fn var_ptr(&self, i: u32) -> Pointer {
|
||||
self.locals[self.var_offset + i as usize]
|
||||
}
|
||||
|
||||
fn temp_ptr(&self, i: u32) -> Pointer {
|
||||
self.locals[self.temp_offset + i as usize]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ extern crate rustc;
|
|||
extern crate rustc_mir;
|
||||
extern crate syntax;
|
||||
|
||||
mod error;
|
||||
pub mod interpreter;
|
||||
mod memory;
|
||||
mod primval;
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::collections::{BTreeMap, HashMap};
|
|||
use std::collections::Bound::{Included, Excluded};
|
||||
use std::ptr;
|
||||
|
||||
use interpreter::{EvalError, EvalResult};
|
||||
use error::{EvalError, EvalResult};
|
||||
use primval::PrimVal;
|
||||
|
||||
const POINTER_SIZE: usize = 8;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue