<commit_before>use std::io::{self, Read, Seek, SeekFrom};
use std::fs::{File, Permissions};
use std::fs::{create_dir_all, set_permissions, hard_link};
use std::os::unix::fs::{PermissionsExt, symlink};
use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};

use tar::Archive;
use flate2::FlateReadExt;
use xz2::read::XzDecoder;
use bzip2::read::BzDecoder;
use libmount::BindMount;

use quire::validate as V;
use container::mount::{unmount};
use builder::context::Context;
use builder::download::{maybe_download_and_check_hashsum};
use builder::commands::generic::run_command_at;
use file_util::{read_visible_entries, create_dir, copy_stream};
use path_util::ToRelative;
use build_step::{BuildStep, VersionError, StepError, Digest, Config, Guard};

#[derive(RustcDecodable, Debug)]
pub struct Tar {
    pub url: String,
    pub sha256: Option<String>,
    pub path: PathBuf,
    pub subdir: PathBuf,
}

impl Tar {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
        .member("url", V::Scalar::new())
        .member("sha256", V::Scalar::new().optional())
        .member("path", V::Directory::new().is_absolute(true).default("/"))
        .member("subdir", V::Directory::new().default("").is_absolute(false))
    }
}

#[derive(RustcDecodable, Debug)]
pub struct TarInstall {
    pub url: String,
    pub sha256: Option<String>,
    pub subdir: Option<PathBuf>,
    pub script: String,
}

impl TarInstall {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
        .member("url", V::Scalar::new())
        .member("sha256", V::Scalar::new().optional())
        .member("subdir", V::Directory::new().optional().is_absolute(false))
        .member("script", V::Scalar::new()
                .default("./configure --prefix=/usr\n\
                          make\n\
                          make install\n"))
    }
}

pub fn unpack_file(_ctx: &mut Context, src: &Path, tgt: &Path,
    includes: &[&Path], excludes: &[&Path])
    -> Result<(), String>
{

    info!("Unpacking {:?} -> {:?}", src, tgt);
    let read_err = |e| format!("Error reading {:?}: {}", src, e);

    let mut file = try!(File::open(&src).map_err(&read_err));

    let mut buf = [0u8; 8];
    let nbytes = try!(file.read(&mut buf).map_err(&read_err));
    try!(file.seek(SeekFrom::Start(0)).map_err(&read_err));
    let magic = &buf[..nbytes];
    if magic.len() >= 2 && magic[..2] == [0x1f, 0x8b] {
        return unpack_stream(
            try!(file.gz_decode().map_err(&read_err)),
            src, tgt, includes, excludes);
    } else if magic.len() >= 6 && magic[..6] ==
        [ 0xFD, b'7', b'z', b'X', b'Z', 0x00]
    {
        return unpack_stream(XzDecoder::new(file),
            src, tgt, includes, excludes);
    } else if magic.len() >= 3 && magic[..3] == [ b'B', b'Z', b'h'] {
        return unpack_stream(BzDecoder::new(file),
            src, tgt, includes, excludes);
    } else {
        return Err(format!("unpacking {:?}: unexpected compression", src));
    }

}

fn unpack_stream<F: Read>(file: F, srcpath: &Path, tgt: &Path,
    includes: &[&Path], excludes: &[&Path])
    -> Result<(), String>
{
    let read_err = |e| format!("Error reading {:?}: {}", srcpath, e);
    let mut arc = Archive::new(file);
    let mut hardlinks = Vec::new();

    for item in try!(arc.entries().map_err(&read_err)) {
        let mut src = try!(item.map_err(&read_err));
        let path_ref = try!(src.path().map_err(&read_err))
            .to_path_buf();
        let mut orig_path: &Path = &path_ref;
        if orig_path.is_absolute() {
            orig_path = orig_path.strip_prefix("/").unwrap();
        }
        if includes.len() > 0 {
            if !includes.iter().any(|x| orig_path.starts_with(x)) {
                continue;
            }
        }
        if excludes.iter().any(|x| orig_path.starts_with(x)) {
            continue;
        }
        let path = tgt.join(orig_path);
        let write_err = |e| format!("Error writing {:?}: {}", path, e);
        let entry = src.header().entry_type();
        if entry.is_dir() {
            try!(create_dir(&path, true).map_err(&write_err));
            let mode = try!(src.header().mode().map_err(&read_err));
            try!(set_permissions(&path, Permissions::from_mode(mode))
                .map_err(&write_err));
        } else if entry.is_symlink() {
            let src = try!(try!(src.header().link_name().map_err(&read_err))
                .ok_or(format!("Error unpacking {:?}, broken symlink", path)));
            match symlink(&src, &path) {
                Ok(_) => {},
                Err(e) => {
                    if e.kind() == io::ErrorKind::NotFound {
                        if let Some(parent) = path.parent() {
                            try!(create_dir(parent, true).map_err(&write_err));
                            try!(symlink(&src, &path).map_err(&write_err))
                        } else {
                            return Err(write_err(e));
                        }
                    } else {
                        return Err(write_err(e));
                    }
                }
            };
        } else if entry.is_hard_link() {
            let link = try!(try!(src.link_name().map_err(&read_err))
                .ok_or(format!("Error unpacking {:?}, broken symlink", path)));
            let link = if link.is_absolute() {
                link.strip_prefix("/").unwrap()
            } else {
                &*link
            };
            hardlinks.push((tgt.join(link).to_path_buf(), path.to_path_buf()));
        } else if entry.is_pax_global_extensions() ||
                  entry.is_pax_local_extensions() ||
                  entry.is_gnu_longname() ||
                  entry.is_gnu_longlink()
        {
            // nothing to do
        } else {
            let mut dest = match File::create(&path) {
                Ok(x) => x,
                Err(e) => {
                    if e.kind() == io::ErrorKind::NotFound {
                        if let Some(parent) = path.parent() {
                            try!(create_dir(parent, true).map_err(&write_err));
                            try!(File::create(&path).map_err(&write_err))
                        } else {
                            return Err(write_err(e));
                        }
                    } else {
                        return Err(write_err(e));
                    }
                }
            };
            try!(copy_stream(&mut src, &mut dest).map_err(|e|
                format!("Error unpacking {:?} -> {:?}: {}",
                        srcpath, path, e)));
            let mode = try!(src.header().mode().map_err(&read_err));
            try!(set_permissions(&path, Permissions::from_mode(mode))
                .map_err(&write_err));
        }
    }
    for (src, dst) in hardlinks.into_iter() {
        let write_err = |e| {
            format!("Error hardlinking {:?} - {:?}: {}", &src, &dst, e)
        };
        match hard_link(&src, &dst) {
            Ok(_) => {},
            Err(e) => {
                if e.kind() == io::ErrorKind::NotFound {
                    if let Some(parent) = dst.parent() {
                        try!(create_dir(parent, true).map_err(&write_err));
                        try!(hard_link(&src, &dst).map_err(&write_err))
                    } else {
                        return Err(write_err(e));
                    }
                } else {
                    return Err(write_err(e));
                }
            }
        };
    }
    Ok(())
}

pub fn tar_command(ctx: &mut Context, tar: &Tar) -> Result<(), String>
{
    let fpath = PathBuf::from("/vagga/root").join(tar.path.rel());
    let filename = try!(maybe_download_and_check_hashsum(
        ctx, &tar.url, tar.sha256.clone()));

    if &Path::new(&tar.subdir) == &Path::new(".") {
        try!(unpack_file(ctx, &filename, &fpath, &[], &[]));
    } else {
        let tmppath = PathBuf::from("/vagga/root/tmp")
            .join(filename.file_name().unwrap());
        let tmpsub = tmppath.join(&tar.subdir);
        try_msg!(create_dir(&tmpsub, true), "Error making dir: {err}");
        if !fpath.exists() {
            try_msg!(create_dir(&fpath, true), "Error making dir: {err}");
        }
        try_msg!(BindMount::new(&fpath, &tmpsub).mount(),
            "temporary tar mount: {err}");
        let res = if tar.subdir.as_path() == Path::new("") {
            unpack_file(ctx, &filename, &tmppath, &[], &[])
        } else {
            unpack_file(ctx, &filename, &tmppath,
                &[&tar.subdir.clone()], &[])
        };
        try!(unmount(&tmpsub));
        try!(res);
    }
    Ok(())
}

pub fn tar_install(ctx: &mut Context, tar: &TarInstall)
    -> Result<(), String>
{
    let filename = try!(maybe_download_and_check_hashsum(
        ctx, &tar.url, tar.sha256.clone()));

    let tmppath = PathBuf::from("/vagga/root/tmp")
        .join(filename.file_name().unwrap());
    try!(create_dir_all(&tmppath)
         .map_err(|e| format!("Error making dir: {}", e)));
    try!(set_permissions(&tmppath, Permissions::from_mode(0o755))
         .map_err(|e| format!("Error setting permissions: {}", e)));
    try!(unpack_file(ctx, &filename, &tmppath, &[], &[]));
    let workdir = if let Some(ref subpath) = tar.subdir {
        tmppath.join(subpath)
    } else {
        let items = try!(read_visible_entries(&tmppath)
            .map_err(|e| format!("Error reading dir: {}", e)));
        if items.len() != 1 {
            if items.len() == 0 {
                return Err("Tar archive was empty".to_string());
            } else {
                return Err("Multiple directories was unpacked. \
                    If thats expected use `subdir: \".\"` or any \
                    other directory".to_string());
            }
        }
        items.into_iter().next().unwrap()
    };
    let workdir = PathBuf::from("/").join(
        workdir.rel_to(&Path::new("/vagga/root")).unwrap());
    return run_command_at(ctx, &[
        "/bin/sh".to_string(),
        "-exc".to_string(),
        tar.script.to_string()],
        &workdir);
}

impl BuildStep for Tar {
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        if let Some(ref sha) = self.sha256 {
            hash.field("hash", sha);
        } else {
            hash.field("url", &self.url);
        }
        hash.field("path", self.path.as_os_str().as_bytes());
        hash.field("subdir", self.subdir.as_os_str().as_bytes());
        Ok(())
    }
    fn build(&self, guard: &mut Guard, build: bool)
        -> Result<(), StepError>
    {
        if build {
            try!(tar_command(&mut guard.ctx, self));
        }
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}

impl BuildStep for TarInstall {
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        if let Some(ref sha) = self.sha256 {
            hash.field("hash", sha);
        } else {
            hash.field("url", &self.url);
        }
        hash.opt_field("subdir",
            &self.subdir.as_ref().map(|x| x.as_os_str().as_bytes()));
        hash.field("script", &self.script);
        Ok(())
    }
    fn build(&self, guard: &mut Guard, build: bool)
        -> Result<(), StepError>
    {
        if build {
            try!(tar_install(&mut guard.ctx, self));
        }
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}
<commit_msg>More expected tar unpacking<commit_after>use std::io::{self, Read, Seek, SeekFrom};
use std::fs::{File, Permissions};
use std::fs::{create_dir_all, set_permissions, hard_link};
use std::os::unix::fs::{PermissionsExt, symlink};
use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};

use tar::Archive;
use flate2::FlateReadExt;
use xz2::read::XzDecoder;
use bzip2::read::BzDecoder;
use libmount::BindMount;

use quire::validate as V;
use container::mount::{unmount};
use builder::context::Context;
use builder::download::{maybe_download_and_check_hashsum};
use builder::commands::generic::run_command_at;
use file_util::{read_visible_entries, create_dir, create_dir_mode, copy_stream};
use path_util::ToRelative;
use build_step::{BuildStep, VersionError, StepError, Digest, Config, Guard};

#[derive(RustcDecodable, Debug)]
pub struct Tar {
    pub url: String,
    pub sha256: Option<String>,
    pub path: PathBuf,
    pub subdir: PathBuf,
}

impl Tar {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
        .member("url", V::Scalar::new())
        .member("sha256", V::Scalar::new().optional())
        .member("path", V::Directory::new().is_absolute(true).default("/"))
        .member("subdir", V::Directory::new().default("").is_absolute(false))
    }
}

#[derive(RustcDecodable, Debug)]
pub struct TarInstall {
    pub url: String,
    pub sha256: Option<String>,
    pub subdir: Option<PathBuf>,
    pub script: String,
}

impl TarInstall {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
        .member("url", V::Scalar::new())
        .member("sha256", V::Scalar::new().optional())
        .member("subdir", V::Directory::new().optional().is_absolute(false))
        .member("script", V::Scalar::new()
                .default("./configure --prefix=/usr\n\
                          make\n\
                          make install\n"))
    }
}

pub fn unpack_file(_ctx: &mut Context, src: &Path, tgt: &Path,
    includes: &[&Path], excludes: &[&Path])
    -> Result<(), String>
{

    info!("Unpacking {:?} -> {:?}", src, tgt);
    let read_err = |e| format!("Error reading {:?}: {}", src, e);

    let mut file = try!(File::open(&src).map_err(&read_err));

    let mut buf = [0u8; 8];
    let nbytes = try!(file.read(&mut buf).map_err(&read_err));
    try!(file.seek(SeekFrom::Start(0)).map_err(&read_err));
    let magic = &buf[..nbytes];
    if magic.len() >= 2 && magic[..2] == [0x1f, 0x8b] {
        return unpack_stream(
            try!(file.gz_decode().map_err(&read_err)),
            src, tgt, includes, excludes);
    } else if magic.len() >= 6 && magic[..6] ==
        [ 0xFD, b'7', b'z', b'X', b'Z', 0x00]
    {
        return unpack_stream(XzDecoder::new(file),
            src, tgt, includes, excludes);
    } else if magic.len() >= 3 && magic[..3] == [ b'B', b'Z', b'h'] {
        return unpack_stream(BzDecoder::new(file),
            src, tgt, includes, excludes);
    } else {
        return Err(format!("unpacking {:?}: unexpected compression", src));
    }

}

fn unpack_stream<F: Read>(file: F, srcpath: &Path, tgt: &Path,
    includes: &[&Path], excludes: &[&Path])
    -> Result<(), String>
{
    let read_err = |e| format!("Error reading {:?}: {}", srcpath, e);
    let mut arc = Archive::new(file);
    let mut hardlinks = Vec::new();

    for item in try!(arc.entries().map_err(&read_err)) {
        let mut src = try!(item.map_err(&read_err));
        let path_ref = try!(src.path().map_err(&read_err))
            .to_path_buf();
        let mut orig_path: &Path = &path_ref;
        if orig_path.is_absolute() {
            orig_path = orig_path.strip_prefix("/").unwrap();
        }
        if includes.len() > 0 {
            if !includes.iter().any(|x| orig_path.starts_with(x)) {
                continue;
            }
        }
        if excludes.iter().any(|x| orig_path.starts_with(x)) {
            continue;
        }
        let path = tgt.join(orig_path);
        let write_err = |e| format!("Error writing {:?}: {}", path, e);
        let entry = src.header().entry_type();
        if entry.is_dir() {
            let mode = try!(src.header().mode().map_err(&read_err));
            try!(create_dir_mode(&path, mode).map_err(&write_err));
        } else if entry.is_symlink() {
            let src = try!(try!(src.header().link_name().map_err(&read_err))
                .ok_or(format!("Error unpacking {:?}, broken symlink", path)));
            match symlink(&src, &path) {
                Ok(_) => {},
                Err(e) => {
                    if e.kind() == io::ErrorKind::NotFound {
                        if let Some(parent) = path.parent() {
                            try!(create_dir(parent, true).map_err(&write_err));
                            try!(symlink(&src, &path).map_err(&write_err))
                        } else {
                            return Err(write_err(e));
                        }
                    } else {
                        return Err(write_err(e));
                    }
                }
            };
        } else if entry.is_hard_link() {
            let link = try!(try!(src.link_name().map_err(&read_err))
                .ok_or(format!("Error unpacking {:?}, broken symlink", path)));
            let link = if link.is_absolute() {
                link.strip_prefix("/").unwrap()
            } else {
                &*link
            };
            hardlinks.push((tgt.join(link).to_path_buf(), path.to_path_buf()));
        } else if entry.is_pax_global_extensions() ||
                  entry.is_pax_local_extensions() ||
                  entry.is_gnu_longname() ||
                  entry.is_gnu_longlink()
        {
            // nothing to do
        } else {
            let mut dest = match File::create(&path) {
                Ok(x) => x,
                Err(e) => {
                    if e.kind() == io::ErrorKind::NotFound {
                        if let Some(parent) = path.parent() {
                            try!(create_dir(parent, true).map_err(&write_err));
                            try!(File::create(&path).map_err(&write_err))
                        } else {
                            return Err(write_err(e));
                        }
                    } else {
                        return Err(write_err(e));
                    }
                }
            };
            try!(copy_stream(&mut src, &mut dest).map_err(|e|
                format!("Error unpacking {:?} -> {:?}: {}",
                        srcpath, path, e)));
            let mode = try!(src.header().mode().map_err(&read_err));
            try!(set_permissions(&path, Permissions::from_mode(mode))
                .map_err(&write_err));
        }
    }
    for (src, dst) in hardlinks.into_iter() {
        let write_err = |e| {
            format!("Error hardlinking {:?} - {:?}: {}", &src, &dst, e)
        };
        match hard_link(&src, &dst) {
            Ok(_) => {},
            Err(e) => {
                if e.kind() == io::ErrorKind::NotFound {
                    if let Some(parent) = dst.parent() {
                        try!(create_dir(parent, true).map_err(&write_err));
                        try!(hard_link(&src, &dst).map_err(&write_err))
                    } else {
                        return Err(write_err(e));
                    }
                } else {
                    return Err(write_err(e));
                }
            }
        };
    }
    Ok(())
}

pub fn tar_command(ctx: &mut Context, tar: &Tar) -> Result<(), String>
{
    let fpath = PathBuf::from("/vagga/root").join(tar.path.rel());
    let filename = try!(maybe_download_and_check_hashsum(
        ctx, &tar.url, tar.sha256.clone()));

    if &Path::new(&tar.subdir) == &Path::new(".") {
        try!(unpack_file(ctx, &filename, &fpath, &[], &[]));
    } else {
        let tmppath = PathBuf::from("/vagga/root/tmp")
            .join(filename.file_name().unwrap());
        let tmpsub = tmppath.join(&tar.subdir);
        try_msg!(create_dir(&tmpsub, true), "Error making dir: {err}");
        if !fpath.exists() {
            try_msg!(create_dir(&fpath, true), "Error making dir: {err}");
        }
        try_msg!(BindMount::new(&fpath, &tmpsub).mount(),
            "temporary tar mount: {err}");
        let res = if tar.subdir.as_path() == Path::new("") {
            unpack_file(ctx, &filename, &tmppath, &[], &[])
        } else {
            unpack_file(ctx, &filename, &tmppath,
                &[&tar.subdir.clone()], &[])
        };
        try!(unmount(&tmpsub));
        try!(res);
    }
    Ok(())
}

pub fn tar_install(ctx: &mut Context, tar: &TarInstall)
    -> Result<(), String>
{
    let filename = try!(maybe_download_and_check_hashsum(
        ctx, &tar.url, tar.sha256.clone()));

    let tmppath = PathBuf::from("/vagga/root/tmp")
        .join(filename.file_name().unwrap());
    try!(create_dir_all(&tmppath)
         .map_err(|e| format!("Error making dir: {}", e)));
    try!(set_permissions(&tmppath, Permissions::from_mode(0o755))
         .map_err(|e| format!("Error setting permissions: {}", e)));
    try!(unpack_file(ctx, &filename, &tmppath, &[], &[]));
    let workdir = if let Some(ref subpath) = tar.subdir {
        tmppath.join(subpath)
    } else {
        let items = try!(read_visible_entries(&tmppath)
            .map_err(|e| format!("Error reading dir: {}", e)));
        if items.len() != 1 {
            if items.len() == 0 {
                return Err("Tar archive was empty".to_string());
            } else {
                return Err("Multiple directories was unpacked. \
                    If thats expected use `subdir: \".\"` or any \
                    other directory".to_string());
            }
        }
        items.into_iter().next().unwrap()
    };
    let workdir = PathBuf::from("/").join(
        workdir.rel_to(&Path::new("/vagga/root")).unwrap());
    return run_command_at(ctx, &[
        "/bin/sh".to_string(),
        "-exc".to_string(),
        tar.script.to_string()],
        &workdir);
}

impl BuildStep for Tar {
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        if let Some(ref sha) = self.sha256 {
            hash.field("hash", sha);
        } else {
            hash.field("url", &self.url);
        }
        hash.field("path", self.path.as_os_str().as_bytes());
        hash.field("subdir", self.subdir.as_os_str().as_bytes());
        Ok(())
    }
    fn build(&self, guard: &mut Guard, build: bool)
        -> Result<(), StepError>
    {
        if build {
            try!(tar_command(&mut guard.ctx, self));
        }
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}

impl BuildStep for TarInstall {
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        if let Some(ref sha) = self.sha256 {
            hash.field("hash", sha);
        } else {
            hash.field("url", &self.url);
        }
        hash.opt_field("subdir",
            &self.subdir.as_ref().map(|x| x.as_os_str().as_bytes()));
        hash.field("script", &self.script);
        Ok(())
    }
    fn build(&self, guard: &mut Guard, build: bool)
        -> Result<(), StepError>
    {
        if build {
            try!(tar_install(&mut guard.ctx, self));
        }
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}
<|endoftext|>


<commit_before><commit_msg>Notes for future work<commit_after><|endoftext|>


<commit_before>use memory::Size;

use super::scope::{ScopeItem, ScopeStack};

/// The arguments that will get passed to a function
/// Distinct to the function's actual type because these are
/// ScopeItems not type defintions
/// Arguments can be assumed to match the type of that function
pub type FuncArgs = Vec<ScopeItem>;

#[derive(Clone)]
pub enum FuncArgType {
    /// A single argument of the given type
    Arg(ItemType),
    /// Any number of arguments of the given type
    Variadic(ItemType),
}

/// An item is anything that can be declared
#[derive(Clone)]
pub enum ItemType {
    /// Any type
    ///TODO: Replace with type bounds when generics are implemented in #45
    Any,

    /// Unit type
    /// The unit type is a type with a single zero-size value.
    /// Both the type and the value are specified: ()
    Unit,

    /// A primitive type is a raw interpretation of some memory cells
    /// These primitive types are built-in and cannot be declared
    /// within the language itself
    Primitive(Size),

    /// A struct has a single definition with any number of
    /// fields and generics
    /// Structs can have impls which contain methods for that
    /// struct
    Struct {
        //TODO: fields, generics, etc.
    },

    /// A fixed-size array
    Array {
        /// The type of the elements in this array
        type_name: String,
        /// The number of elements that this array can hold
        size: usize,
    },

    /// Definition of a function's type
    Function {
        args: Vec<FuncArgType>,
        return_type: Box<ItemType>,
    },
}

impl ItemType {
    /// Computes the required size in cells of an *instance* of this type
    pub fn required_size(&self, scope: &ScopeStack) -> Size {
        match *self {
            ItemType::Unit => Size::default(),
            ItemType::Primitive(size) => size,
            ItemType::Struct { .. } => Size::default(), //TODO: Update this when fields are supported
            _ => unimplemented!(),
        }
    }
}
<commit_msg>Using correct type for the type of items in an array<commit_after>use memory::Size;

use super::scope::{ScopeItem, ScopeStack};

/// The arguments that will get passed to a function
/// Distinct to the function's actual type because these are
/// ScopeItems not type defintions
/// Arguments can be assumed to match the type of that function
pub type FuncArgs = Vec<ScopeItem>;

#[derive(Clone)]
pub enum FuncArgType {
    /// A single argument of the given type
    Arg(ItemType),
    /// Any number of arguments of the given type
    Variadic(ItemType),
}

/// An item is anything that can be declared
#[derive(Clone)]
pub enum ItemType {
    /// Any type
    ///TODO: Replace with type bounds when generics are implemented in #45
    Any,

    /// Unit type
    /// The unit type is a type with a single zero-size value.
    /// Both the type and the value are specified: ()
    Unit,

    /// A primitive type is a raw interpretation of some memory cells
    /// These primitive types are built-in and cannot be declared
    /// within the language itself
    Primitive(Size),

    /// A struct has a single definition with any number of
    /// fields and generics
    /// Structs can have impls which contain methods for that
    /// struct
    Struct {
        //TODO: fields, generics, etc.
    },

    /// A fixed-size array
    Array {
        /// The type of the elements in this array
        type_id: TypeId,
        /// The number of elements that this array can hold
        size: usize,
    },

    /// Definition of a function's type
    Function {
        args: Vec<FuncArgType>,
        return_type: Box<ItemType>,
    },
}

impl ItemType {
    /// Computes the required size in cells of an *instance* of this type
    pub fn required_size(&self, scope: &ScopeStack) -> Size {
        match *self {
            ItemType::Unit => Size::default(),
            ItemType::Primitive(size) => size,
            ItemType::Struct { .. } => Size::default(), //TODO: Update this when fields are supported
            _ => unimplemented!(),
        }
    }
}
<|endoftext|>


<commit_before>use std::num::Float;

fn main() {
    let ceiling: i32 = 1000;
    let mut primes: [i32; 1000] = [0; 1000];
    let mut found: i32 = 0;
    let test: i32 = 3;
    let increment: i32 = 2;
    let testRoot: f64;
    let isPrime: bool;
    primes[0] = 2;
    found += 1;
    while !(found < ceiling) {
        testRoot = Float::sqrt(test);
        isPrime = true;
        for prime in 0..found {
            if testRoot <= primes[prime] as f32 {
                isPrime = false;
                break;
            }
        }
        if isPrime {
            primes[found] = test;
            found += 1;
        }
        test += increment;
    }
}
<commit_msg>More work on figuring out Rust<commit_after>use std::num::Float;

fn main() {
    let ceiling: i32 = 1000;
    let mut primes: [i32; 1000] = [0; 1000];
    let mut found: i32 = 0;
    let test: i32 = 3;
    let increment: i32 = 2;
    let testRoot: f64;
    let isPrime: bool;
    primes[0] = 2;
    found += 1;
    while !(found < ceiling) {
        testRoot = (test as f64).sqrt();
        isPrime = true;
        for prime in primes.iter() {
            if prime == 0 {
                break;
            }
            if testRoot <= (prime.to_f64()) {
                isPrime = false;
                break;
            }
            if test % prime == 0 {
                isPrime = false;
                break;
            }
        }
        if isPrime {
            primes[found] = test;
            found += 1;
        }
        test += increment;
    }
}
<|endoftext|>


<commit_before>/// Custom `Error` and `Result` types, declared using `error-chain`.

use nom::IResult;
use std::default::Default;
use std::fmt;
use std::io;
use std::path::PathBuf;

error_chain! {
    foreign_links {
        Io(io::Error);
    }

    errors {
        /// Our input data ended sooner than we expected.
        IncompleteInput {
            description("Input ended unexpectedly")
            display("Input ended unexpectedly")
        }

        /// We were unable to find a required key in an `*.idx` file.
        MissingKey(key: &'static str) {
            description("Could not find required key")
            display("Could not find required key '{}'", key)
        }

        /// We could not parse a value.
        Parse(message: String) {
            description("Parse error")
            display("Could not parse: {}", &message)
        }

        /// We could not parse a file for some reason.
        ReadFile(path: PathBuf) {
            description("Could not read file")
            display("Could not read {}", path.display())
        }

        /// We have leftover input that we didn't expect.
        UnexpectedInput {
            description("Unexpected extra input")
            display("Unexpected extra input")
        }
    }
}

pub trait IResultExt<I, O, E> {
    fn ignore_trailing_data(self) -> IResult<I, O, E>;
    fn to_vobsub_result(self) -> Result<O>;
}

impl<I: Default+Eq, O, E: fmt::Debug> IResultExt<I, O, E> for IResult<I, O, E> {
    fn ignore_trailing_data(self) -> IResult<I, O, E> {
        match self {
            IResult::Done(_, val) => IResult::Done(I::default(), val),
            other => other,
        }
    }

    fn to_vobsub_result(self) -> Result<O> {
        match self {
            IResult::Done(rest, val) => {
                if rest == I::default() {
                    Ok(val)
                } else {
                    Err(ErrorKind::UnexpectedInput.into())
                }
            }
            IResult::Incomplete(_) => Err(ErrorKind::IncompleteInput.into()),
            IResult::Error(err) => {
                Err(ErrorKind::Parse(format!("{:?}", err)).into())
            }
        }
    }
}
<commit_msg>Fix documentation warning<commit_after>//! Custom `Error` and `Result` types, declared using `error-chain`.

// There's no way for us to document `ErrorrKind::Io`, unfortunately.
#![allow(missing_docs)]

use nom::IResult;
use std::default::Default;
use std::fmt;
use std::io;
use std::path::PathBuf;

error_chain! {
    foreign_links {
        Io(io::Error);
    }

    errors {
        /// Our input data ended sooner than we expected.
        IncompleteInput {
            description("Input ended unexpectedly")
            display("Input ended unexpectedly")
        }

        /// We were unable to find a required key in an `*.idx` file.
        MissingKey(key: &'static str) {
            description("Could not find required key")
            display("Could not find required key '{}'", key)
        }

        /// We could not parse a value.
        Parse(message: String) {
            description("Parse error")
            display("Could not parse: {}", &message)
        }

        /// We could not parse a file for some reason.
        ReadFile(path: PathBuf) {
            description("Could not read file")
            display("Could not read {}", path.display())
        }

        /// We have leftover input that we didn't expect.
        UnexpectedInput {
            description("Unexpected extra input")
            display("Unexpected extra input")
        }
    }
}

pub trait IResultExt<I, O, E> {
    fn ignore_trailing_data(self) -> IResult<I, O, E>;
    fn to_vobsub_result(self) -> Result<O>;
}

impl<I: Default+Eq, O, E: fmt::Debug> IResultExt<I, O, E> for IResult<I, O, E> {
    fn ignore_trailing_data(self) -> IResult<I, O, E> {
        match self {
            IResult::Done(_, val) => IResult::Done(I::default(), val),
            other => other,
        }
    }

    fn to_vobsub_result(self) -> Result<O> {
        match self {
            IResult::Done(rest, val) => {
                if rest == I::default() {
                    Ok(val)
                } else {
                    Err(ErrorKind::UnexpectedInput.into())
                }
            }
            IResult::Incomplete(_) => Err(ErrorKind::IncompleteInput.into()),
            IResult::Error(err) => {
                Err(ErrorKind::Parse(format!("{:?}", err)).into())
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Module that contains a crawler function that crawls through the vulkan xml and returns a
//! Registry struct
use xml::reader::{Events, XmlEvent};
use xml::name::OwnedName;
use xml::attribute::OwnedAttribute;
use std::io::Read;
use std::ffi::CStr;
use {VkRegistry, VkType, VkMember};
use VkVariant;

pub fn crawl<R: Read>(xml_events: Events<R>, mut registry: VkRegistry) -> VkRegistry {
    use self::XmlElement::*;

    let mut type_buffer = VkType::Unhandled;
    let mut cur_block = VkBlock::None;
    // A variable that contains what the index of the element in vk_elements that vk_elements was
    // popped to. Used to prevent the element iterator from going over elements that have already
    // been checked.
    let mut popped_to = 0;

    // A stack of the relevant elements in the vulkan xml
    let mut vk_elements = Vec::with_capacity(10);

    for event in xml_events {

        let event = event.unwrap();
        match event {
            XmlEvent::StartElement{ name, attributes, .. } => {
                vk_elements.push(XmlElement::new_tag(name, attributes));
            }

            XmlEvent::EndElement{ name } => {
                for el in &vk_elements[popped_to..] {
                    match *el {
                        Tag{name: ref tag_name, attributes: ref tag_attrs} =>
                            match &tag_name[..] {
                                "types"      => cur_block = VkBlock::Types,
                                "enums"      => cur_block = VkBlock::Enums,
                                "commands"   => cur_block = VkBlock::Commands,
                                "extensions" => cur_block = VkBlock::Extensions,

                                "type"  =>
                                    if VkBlock::Types == cur_block {
                                        if let Some(category) = find_attribute(tag_attrs, "category") {
                                            registry.push_type(type_buffer).ok();
                                            match category {
                                                "basetype"      => type_buffer = VkType::Unhandled,
                                                "bitmask"       => type_buffer = VkType::Unhandled,
                                                "define"        => type_buffer = VkType::Unhandled,
                                                "enum"          => type_buffer = VkType::Unhandled,
                                                "funcpointer"   => type_buffer = VkType::Unhandled,
                                                "group"         => type_buffer = VkType::Unhandled,
                                                "handle"        => type_buffer = VkType::Unhandled,
                                                "include"       => type_buffer = VkType::Unhandled,
                                                "struct"        =>
                                                    type_buffer = VkType::Struct{name: registry.append_str(find_attribute(tag_attrs, "name").unwrap()), fields: Vec::with_capacity(8)},
                                                "union"         =>
                                                    type_buffer = VkType::Union{name: registry.append_str(find_attribute(tag_attrs, "name").unwrap()), variants: Vec::with_capacity(8)},
                                                _               => panic!("Unexpected category")
                                            }
                                        }
                                    } else {/*panic!("\"type\" tag found outside of \"types\" block")*/}, //TODO: implement fully so panic can be used

                                "member" =>
                                    if VkBlock::Types == cur_block {
                                        match type_buffer {
                                            VkType::Struct{ref mut fields, ..} => fields.push(VkMember::empty()),
                                            VkType::Union{ref mut variants, ..} => variants.push(VkMember::empty()),
                                            _ =>
                                                panic!("Unexpected \"member\" tag found")
                                        }
                                    } else {panic!("\"member\" tag found outside of \"types\" block")},

                                _ => ()
                            },

                        Characters{ref chars, tag} =>
                            match tag {
                                "type" =>
                                    if VkBlock::Types == cur_block {
                                        match type_buffer {
                                            VkType::Struct{fields: ref mut members, ..} |
                                            VkType::Union{variants: ref mut members, ..} => members.last_mut().unwrap().set_type(registry.append_str(&chars[..])),
                                            _ => ()
                                        }
                                    },
                                _ => ()
                            }
                    }
                }

                pop_element_stack(&mut vk_elements);
                popped_to = vk_elements.len();
            }

            XmlEvent::Characters(chars) => {
                let tag_ptr =
                    match *vk_elements.last().unwrap() {
                        XmlElement::Tag{name: ref tag, ..} => &tag[..] as *const str,
                        // In circumstances where the xml reads something like <foo>foostart<bar>bar</bar>fooend</foo>, the
                        // "fooend" string will appear in vk_elements after the "foostart" event, instead of after <foo>.
                        // Because of that, we must pull the tag from the other Characters event.
                        XmlElement::Characters{tag, ..} => tag as *const str
                    };

                unsafe{ vk_elements.push(XmlElement::new_chars(chars, &*tag_ptr)) }
            }

            _ => ()
        }
    }

    for t in &registry.types {
        unsafe {
            match *t {
                VkType::Struct{name, ref fields} => {
                    println!("Struct {:?}", CStr::from_ptr(name));

                    for f in fields {
                        println!("type: {:?}", f.field_type);
                    }
                }
                VkType::Union{name, ref variants} => {
                    println!("Union {:?}", CStr::from_ptr(name));

                    for v in variants {
                        println!("type: {:?}", v.field_type);
                    }
                }
                _ => ()
            }
        }
    }

    registry
}

fn find_attribute<'v>(source: &'v Vec<OwnedAttribute>, query: &str) -> Option<&'v str> {
    source.into_iter().skip_while(|attr| &attr.name.local_name != query).next().map(|res| &*res.value)
}

/// Takes a mutable reference to a XmlElement stack, popping the stack of Character elements until
/// it pops a Tag
fn pop_element_stack(vk_elements: &mut Vec<XmlElement>) {
    match vk_elements.pop() {
        Some(el) =>
            if let XmlElement::Characters{..} = el {
                pop_element_stack(vk_elements)
            },

        None => panic!("Invalid xml; Unexpected closing tag")
    }
}

#[derive(Debug)]
enum XmlElement<'a> {
    Tag {
        /// The name of the tag e.g. "hello" in <hello>
        name: String,
        /// The attributes contained within the specification xml.
        attributes: Vec<OwnedAttribute>
    },
    Characters{
        chars: String,
        tag: &'a str
    }
}

impl<'a> XmlElement<'a> {
    fn new_tag(name: OwnedName, attributes: Vec<OwnedAttribute>) -> XmlElement<'a> {
        XmlElement::Tag {
            name: name.local_name,
            attributes: attributes
        }
    }

    fn new_chars(chars: String, tag: &'a str) -> XmlElement<'a> {
        XmlElement::Characters {
            chars: chars,
            tag: tag
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
enum VkBlock {
    Types,
    Enums,
    Commands,
    Extensions,
    None
}<commit_msg>Added pointer detection functionality to the crawler<commit_after>//! Module that contains a crawler function that crawls through the vulkan xml and returns a
//! Registry struct
use xml::reader::{Events, XmlEvent};
use xml::name::OwnedName;
use xml::attribute::OwnedAttribute;
use std::io::Read;
use std::ffi::CStr;
use {VkRegistry, VkType, VkMember};
use VkVariant;

pub fn crawl<R: Read>(xml_events: Events<R>, mut registry: VkRegistry) -> VkRegistry {
    use self::XmlElement::*;

    let mut type_buffer = VkType::Unhandled;
    let mut cur_block = VkBlock::None;
    // A variable that contains what the index of the element in vk_elements that vk_elements was
    // popped to. Used to prevent the element iterator from going over elements that have already
    // been checked.
    let mut popped_to = 0;

    // A stack of the relevant elements in the vulkan xml
    let mut vk_elements = Vec::with_capacity(10);

    for event in xml_events {

        let event = event.unwrap();
        match event {
            XmlEvent::StartElement{ name, attributes, .. } => {
                vk_elements.push(XmlElement::new_tag(name, attributes));
            }

            XmlEvent::EndElement{ name } => {
                for el in &vk_elements[popped_to..] {
                    match *el {
                        Tag{name: ref tag_name, attributes: ref tag_attrs} =>
                            match &tag_name[..] {
                                "types"      => cur_block = VkBlock::Types,
                                "enums"      => cur_block = VkBlock::Enums,
                                "commands"   => cur_block = VkBlock::Commands,
                                "extensions" => cur_block = VkBlock::Extensions,

                                "type"  =>
                                    if VkBlock::Types == cur_block {
                                        if let Some(category) = find_attribute(tag_attrs, "category") {
                                            registry.push_type(type_buffer).ok();
                                            match category {
                                                "basetype"      => type_buffer = VkType::Unhandled,
                                                "bitmask"       => type_buffer = VkType::Unhandled,
                                                "define"        => type_buffer = VkType::Unhandled,
                                                "enum"          => type_buffer = VkType::Unhandled,
                                                "funcpointer"   => type_buffer = VkType::Unhandled,
                                                "group"         => type_buffer = VkType::Unhandled,
                                                "handle"        => type_buffer = VkType::Unhandled,
                                                "include"       => type_buffer = VkType::Unhandled,
                                                "struct"        =>
                                                    type_buffer = VkType::Struct{name: registry.append_str(find_attribute(tag_attrs, "name").unwrap()), fields: Vec::with_capacity(8)},
                                                "union"         =>
                                                    type_buffer = VkType::Union{name: registry.append_str(find_attribute(tag_attrs, "name").unwrap()), variants: Vec::with_capacity(8)},
                                                _               => panic!("Unexpected category")
                                            }
                                        }
                                    } else {/*panic!("\"type\" tag found outside of \"types\" block")*/}, //TODO: implement fully so panic can be used

                                "member" =>
                                    if VkBlock::Types == cur_block {
                                        match type_buffer {
                                            VkType::Struct{ref mut fields, ..} => fields.push(VkMember::empty()),
                                            VkType::Union{ref mut variants, ..} => variants.push(VkMember::empty()),
                                            _ =>
                                                panic!("Unexpected \"member\" tag found")
                                        }
                                    } else {panic!("\"member\" tag found outside of \"types\" block")},

                                _ => ()
                            },

                        Characters{ref chars, tag} =>
                            match tag {
                                "type" =>
                                    if VkBlock::Types == cur_block {
                                        match type_buffer {
                                            VkType::Struct{fields: ref mut members, ..} |
                                            VkType::Union{variants: ref mut members, ..} => members.last_mut().unwrap().set_type(registry.append_str(&chars[..])),
                                            _ => ()
                                        }
                                    },
                                "member" =>
                                    if VkBlock::Types == cur_block {
                                        match type_buffer {
                                            VkType::Struct{fields: ref mut members, ..} |
                                            VkType::Union{variants: ref mut members, ..} =>
                                                match chars.trim() {
                                                    "const" => members.last_mut().unwrap().change_type_const(),
                                                    "*"     => members.last_mut().unwrap().change_type_ptr(),
                                                    _       => ()
                                                },
                                            _ => ()
                                        }
                                    },
                                _ => ()
                            }
                    }
                }

                pop_element_stack(&mut vk_elements);
                popped_to = vk_elements.len();
            }

            XmlEvent::Characters(chars) => {
                let tag_ptr =
                    match *vk_elements.last().unwrap() {
                        XmlElement::Tag{name: ref tag, ..} => &tag[..] as *const str,
                        // In circumstances where the xml reads something like <foo>foostart<bar>bar</bar>fooend</foo>, the
                        // "fooend" string will appear in vk_elements after the "foostart" event, instead of after <foo>.
                        // Because of that, we must pull the tag from the other Characters event.
                        XmlElement::Characters{tag, ..} => tag as *const str
                    };

                unsafe{ vk_elements.push(XmlElement::new_chars(chars, &*tag_ptr)) }
            }

            _ => ()
        }
    }

    for t in &registry.types {
        unsafe {
            match *t {
                VkType::Struct{name, ref fields} => {
                    println!("Struct {:?}", CStr::from_ptr(name));

                    for f in fields {
                        println!("type: {:?}", f.field_type);
                    }
                }
                VkType::Union{name, ref variants} => {
                    println!("Union {:?}", CStr::from_ptr(name));

                    for v in variants {
                        println!("type: {:?}", v.field_type);
                    }
                }
                _ => ()
            }
        }
    }

    registry
}

fn find_attribute<'v>(source: &'v Vec<OwnedAttribute>, query: &str) -> Option<&'v str> {
    source.into_iter().skip_while(|attr| &attr.name.local_name != query).next().map(|res| &*res.value)
}

/// Takes a mutable reference to a XmlElement stack, popping the stack of Character elements until
/// it pops a Tag
fn pop_element_stack(vk_elements: &mut Vec<XmlElement>) {
    match vk_elements.pop() {
        Some(el) =>
            if let XmlElement::Characters{..} = el {
                pop_element_stack(vk_elements)
            },

        None => panic!("Invalid xml; Unexpected closing tag")
    }
}

#[derive(Debug)]
enum XmlElement<'a> {
    Tag {
        /// The name of the tag e.g. "hello" in <hello>
        name: String,
        /// The attributes contained within the specification xml.
        attributes: Vec<OwnedAttribute>
    },
    Characters{
        chars: String,
        tag: &'a str
    }
}

impl<'a> XmlElement<'a> {
    fn new_tag(name: OwnedName, attributes: Vec<OwnedAttribute>) -> XmlElement<'a> {
        XmlElement::Tag {
            name: name.local_name,
            attributes: attributes
        }
    }

    fn new_chars(chars: String, tag: &'a str) -> XmlElement<'a> {
        XmlElement::Characters {
            chars: chars,
            tag: tag
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
enum VkBlock {
    Types,
    Enums,
    Commands,
    Extensions,
    None
}<|endoftext|>


<commit_before>use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::{
    Local, Location, Place, PlaceBase, Statement, StatementKind, TerminatorKind
};

use rustc_data_structures::fx::FxHashSet;

use crate::borrow_check::MirBorrowckCtxt;

impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
    /// Walks the MIR adding to the set of `used_mut` locals that will be ignored for the purposes
    /// of the `unused_mut` lint.
    ///
    /// `temporary_used_locals` should contain locals that were found to be temporary, mutable and
    ///  used from borrow checking. This function looks for assignments into these locals from
    ///  user-declared locals and adds those user-defined locals to the `used_mut` set. This can
    ///  occur due to a rare case involving upvars in closures.
    ///
    /// `never_initialized_mut_locals` should contain the set of user-declared mutable locals
    ///  (not arguments) that have not already been marked as being used.
    ///  This function then looks for assignments from statements or the terminator into the locals
    ///  from this set and removes them from the set. This leaves only those locals that have not
    ///  been assigned to - this set is used as a proxy for locals that were not initialized due to
    ///  unreachable code. These locals are then considered "used" to silence the lint for them.
    ///  See #55344 for context.
    crate fn gather_used_muts(
        &mut self,
        temporary_used_locals: FxHashSet<Local>,
        mut never_initialized_mut_locals: FxHashSet<Local>,
    ) {
        {
            let mut visitor = GatherUsedMutsVisitor {
                temporary_used_locals,
                never_initialized_mut_locals: &mut never_initialized_mut_locals,
                mbcx: self,
            };
            visitor.visit_body(visitor.mbcx.mir);
        }

        // Take the union of the existed `used_mut` set with those variables we've found were
        // never initialized.
        debug!("gather_used_muts: never_initialized_mut_locals={:?}", never_initialized_mut_locals);
        self.used_mut = self.used_mut.union(&never_initialized_mut_locals).cloned().collect();
    }
}

/// MIR visitor for collecting used mutable variables.
/// The 'visit lifetime represents the duration of the MIR walk.
struct GatherUsedMutsVisitor<'visit, 'cx: 'visit, 'gcx: 'tcx, 'tcx: 'cx> {
    temporary_used_locals: FxHashSet<Local>,
    never_initialized_mut_locals: &'visit mut FxHashSet<Local>,
    mbcx: &'visit mut MirBorrowckCtxt<'cx, 'gcx, 'tcx>,
}

impl GatherUsedMutsVisitor<'_, '_, '_, '_> {
    fn remove_never_initialized_mut_locals(into: &Place) {
        // Remove any locals that we found were initialized from the
        // `never_initialized_mut_locals` set. At the end, the only remaining locals will
        // be those that were never initialized - we will consider those as being used as
        // they will either have been removed by unreachable code optimizations; or linted
        // as unused variables.
        if let Some(local) = into.base_local() {
            debug!(
                "visit_statement: statement={:?} local={:?} \
                 never_initialized_mut_locals={:?}",
                statement, local, self.never_initialized_mut_locals
            );
            let _ = self.never_initialized_mut_locals.remove(&local);
        }
    }
}

impl<'visit, 'cx, 'gcx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'gcx, 'tcx> {
    fn visit_terminator_kind(
        &mut self,
        kind: &TerminatorKind<'tcx>,
        _location: Location,
    ) {
        debug!("visit_terminator_kind: kind={:?}", kind);
        match &kind {
            TerminatorKind::Call { destination: Some((into, _)), .. } => {
                self.remove_never_initialized_mut_locals(&into);
            },
            TerminatorKind::DropAndReplace { location, .. } => {
                self.remove_never_initialized_mut_locals(&location);
            },
            _ => {},
        }
    }

    fn visit_statement(
        &mut self,
        statement: &Statement<'tcx>,
        _location: Location,
    ) {
        match &statement.kind {
            StatementKind::Assign(into, _) => {
                self.remove_never_initialized_mut_locals(into);
            },
            _ => {},
        }
    }

    fn visit_local(
        &mut self,
        local: &Local,
        place_context: PlaceContext,
        location: Location,
    ) {
        if place_context.is_place_assignment() && self.temporary_used_locals.contains(local) {
            // Propagate the Local assigned at this Location as a used mutable local variable
            for moi in &self.mbcx.move_data.loc_map[location] {
                let mpi = &self.mbcx.move_data.moves[*moi].path;
                let path = &self.mbcx.move_data.move_paths[*mpi];
                debug!(
                    "assignment of {:?} to {:?}, adding {:?} to used mutable set",
                    path.place, local, path.place
                );
                if let Place::Base(PlaceBase::Local(user_local)) = path.place {
                    self.mbcx.used_mut.insert(user_local);
                }
            }
        }
    }
}
<commit_msg>Add self parameter<commit_after>use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::{
    Local, Location, Place, PlaceBase, Statement, StatementKind, TerminatorKind
};

use rustc_data_structures::fx::FxHashSet;

use crate::borrow_check::MirBorrowckCtxt;

impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
    /// Walks the MIR adding to the set of `used_mut` locals that will be ignored for the purposes
    /// of the `unused_mut` lint.
    ///
    /// `temporary_used_locals` should contain locals that were found to be temporary, mutable and
    ///  used from borrow checking. This function looks for assignments into these locals from
    ///  user-declared locals and adds those user-defined locals to the `used_mut` set. This can
    ///  occur due to a rare case involving upvars in closures.
    ///
    /// `never_initialized_mut_locals` should contain the set of user-declared mutable locals
    ///  (not arguments) that have not already been marked as being used.
    ///  This function then looks for assignments from statements or the terminator into the locals
    ///  from this set and removes them from the set. This leaves only those locals that have not
    ///  been assigned to - this set is used as a proxy for locals that were not initialized due to
    ///  unreachable code. These locals are then considered "used" to silence the lint for them.
    ///  See #55344 for context.
    crate fn gather_used_muts(
        &mut self,
        temporary_used_locals: FxHashSet<Local>,
        mut never_initialized_mut_locals: FxHashSet<Local>,
    ) {
        {
            let mut visitor = GatherUsedMutsVisitor {
                temporary_used_locals,
                never_initialized_mut_locals: &mut never_initialized_mut_locals,
                mbcx: self,
            };
            visitor.visit_body(visitor.mbcx.mir);
        }

        // Take the union of the existed `used_mut` set with those variables we've found were
        // never initialized.
        debug!("gather_used_muts: never_initialized_mut_locals={:?}", never_initialized_mut_locals);
        self.used_mut = self.used_mut.union(&never_initialized_mut_locals).cloned().collect();
    }
}

/// MIR visitor for collecting used mutable variables.
/// The 'visit lifetime represents the duration of the MIR walk.
struct GatherUsedMutsVisitor<'visit, 'cx: 'visit, 'gcx: 'tcx, 'tcx: 'cx> {
    temporary_used_locals: FxHashSet<Local>,
    never_initialized_mut_locals: &'visit mut FxHashSet<Local>,
    mbcx: &'visit mut MirBorrowckCtxt<'cx, 'gcx, 'tcx>,
}

impl GatherUsedMutsVisitor<'_, '_, '_, '_> {
    fn remove_never_initialized_mut_locals(&mut self, into: &Place) {
        // Remove any locals that we found were initialized from the
        // `never_initialized_mut_locals` set. At the end, the only remaining locals will
        // be those that were never initialized - we will consider those as being used as
        // they will either have been removed by unreachable code optimizations; or linted
        // as unused variables.
        if let Some(local) = into.base_local() {
            debug!(
                "visit_statement: statement={:?} local={:?} \
                 never_initialized_mut_locals={:?}",
                statement, local, self.never_initialized_mut_locals
            );
            let _ = self.never_initialized_mut_locals.remove(&local);
        }
    }
}

impl<'visit, 'cx, 'gcx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'gcx, 'tcx> {
    fn visit_terminator_kind(
        &mut self,
        kind: &TerminatorKind<'tcx>,
        _location: Location,
    ) {
        debug!("visit_terminator_kind: kind={:?}", kind);
        match &kind {
            TerminatorKind::Call { destination: Some((into, _)), .. } => {
                self.remove_never_initialized_mut_locals(&into);
            },
            TerminatorKind::DropAndReplace { location, .. } => {
                self.remove_never_initialized_mut_locals(&location);
            },
            _ => {},
        }
    }

    fn visit_statement(
        &mut self,
        statement: &Statement<'tcx>,
        _location: Location,
    ) {
        match &statement.kind {
            StatementKind::Assign(into, _) => {
                self.remove_never_initialized_mut_locals(into);
            },
            _ => {},
        }
    }

    fn visit_local(
        &mut self,
        local: &Local,
        place_context: PlaceContext,
        location: Location,
    ) {
        if place_context.is_place_assignment() && self.temporary_used_locals.contains(local) {
            // Propagate the Local assigned at this Location as a used mutable local variable
            for moi in &self.mbcx.move_data.loc_map[location] {
                let mpi = &self.mbcx.move_data.moves[*moi].path;
                let path = &self.mbcx.move_data.move_paths[*mpi];
                debug!(
                    "assignment of {:?} to {:?}, adding {:?} to used mutable set",
                    path.place, local, path.place
                );
                if let Place::Base(PlaceBase::Local(user_local)) = path.place {
                    self.mbcx.used_mut.insert(user_local);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Paging
//!
//! The `x86_64` architecture uses a four-level page table structure. The top
//! page table is called the Page Meta-Level 4 (PML4) table, followed by
//! the Page Directory Pointer Table (PDPT), Page Directory (PD) table, and
//! finally the bottom-level Page Table (PT).
use core::ops;
use core::ptr::Unique;

use multiboot2;

use arch::cpu::control_regs::cr3;

use memory::VAddr;
use memory::paging::{Page, VirtualPage, Mapper};
use memory::alloc::FrameAllocator;

use super::{PhysicalPage, PAddr, PAGE_SIZE};

use self::table::*;
use self::temp::TempPage;

pub mod table;
pub mod tlb;
pub mod temp;

pub struct ActivePageTable { pml4: ActivePML4 }

impl ops::Deref for ActivePageTable {
    type Target = ActivePML4;

    fn deref(&self) -> &ActivePML4 {
        &self.pml4
    }
}

impl ops::DerefMut for ActivePageTable {
    fn deref_mut(&mut self) -> &mut ActivePML4 {
        &mut self.pml4
    }
}

impl ActivePageTable {
    pub unsafe fn new() -> ActivePageTable {
        ActivePageTable { pml4: ActivePML4::new() }
    }

    /// Execute a closure with the recursive mapping temporarily changed to a
    /// new page table
    pub fn using<F>( &mut self
                   , table: &mut InactivePageTable
                   , temp_page: &mut temp::TempPage
                   , f: F)
    where F: FnOnce(&mut ActivePML4) {
        use self::tlb::flush_all;
        {
            // back up the current PML4 frame
            let prev_pml4_frame = unsafe {
                // this is safe to execute; we are in kernel mode
                cr3::current_pagetable_frame()
            };

            // remap the 511th PML4 entry (the recursive entry) to map to the // frame containing the new PML4.
            self.pml4_mut()[511]
                .set(table.pml4_frame, PRESENT | WRITABLE);
            unsafe {
                // this is safe to execute; we are in kernel mode
                flush_all();
            }

            // execute the closure
            f(self);

            // remap the 511th entry to point back to the original frame
            self.pml4_mut()[511]
                .set(prev_pml4_frame, PRESENT | WRITABLE);

            unsafe {
                // this is safe to execute; we are in kernel mode
                flush_all();
            }
        }
        temp_page.unmap(self);

    }

    /// Replace the current `ActivePageTable` with the given `InactivePageTable`
    ///
    /// # Arguments:
    /// + `new_table`: the `InactivePageTable` that will replace the current
    ///                `ActivePageTable`.
    ///
    /// # Returns:
    /// + the old active page table as an `InactivePageTable`.
    pub fn replace_with(&mut self, new_table: &mut InactivePageTable)
                       -> InactivePageTable {
        unsafe {
            // this is safe to execute; we are in kernel mode
            let old_pml4_frame = cr3::current_pagetable_frame();

            cr3::set_pagetable_frame(new_table.pml4_frame);

            InactivePageTable {
                pml4_frame: old_pml4_frame
            }
        }
    }

}

/// Struct representing the currently active PML4 instance.
///
/// The `ActivePML4` is a `Unique` reference to a PML4-level page table. It's
/// unique because, well, there can only be one active PML4 at a given time.
///
///
pub struct ActivePML4(Unique<Table<PML4Level>>);

/// The active PML4 table is the single point of entry for page mapping.
impl Mapper for ActivePML4 {
    type Flags = EntryFlags;

    fn translate(&self, vaddr: VAddr) -> Option<PAddr> {
        let offset = *vaddr % PAGE_SIZE as usize;
        self.translate_page(Page::containing(vaddr))
            .map(|frame| PAddr::from(frame.number + offset as u64) )
    }

    fn translate_page(&self, page: VirtualPage) -> Option<PhysicalPage> {
        let addr = page.base();
        let pdpt = self.pml4().next_table(addr);

        let huge_page = || {
            pdpt.and_then(|pdpt|
                pdpt[addr]
                    .do_huge(PDLevel::index_of(addr) + PTLevel::index_of(addr))
                    .or_else(|| {
                        pdpt.next_table(addr).and_then(|pd|
                            pd[addr].do_huge(PTLevel::index_of(addr))
                        )
                    })
                )
        };

        pdpt.and_then(|pdpt| pdpt.next_table(addr))
            .and_then(|pd| pd.next_table(addr))
            .and_then(|pt| pt[addr].get_frame())
            .or_else(huge_page)
    }

    /// Modifies the page tables so that `page` maps to `frame`.
    ///
    /// # Arguments
    /// + `page`: the virtual `Page` to map
    /// + `frame`: the physical `Frame` that `Page` should map to.
    /// + `flags`: the page table entry flags.
    /// + `alloc`: a memory allocator
    fn map<A>( &mut self, page: VirtualPage, frame: PhysicalPage
             , flags: EntryFlags, alloc: &A)
    where A: FrameAllocator {
        // base virtual address of page being mapped
        let addr = page.base();

        // access or create all the lower-level page tables.
        let mut page_table
            // get the PML4
            = self.pml4_mut()
                  // get or create the PDPT table at the page's PML4 index
                  .create_next(addr, alloc)
                  // get or create the PD table at the page's PDPT index
                  .create_next(addr, alloc)
                  // get or create the page table at the  page's PD table index
                  .create_next(addr, alloc);

        // check if the page at that index is not currently in use, as we
        // cannot map a page which is currently in use.
        assert!(page_table[addr].is_unused()
               , "Could not map frame {:?}, page table entry {} is already \
                  in use!", frame, PTLevel::index_of(addr));
        // set the page table entry at that index
        page_table[addr].set(frame, flags | table::PRESENT);
    }

    fn identity_map<A>(&mut self, frame: PhysicalPage, flags: EntryFlags
                      , alloc: &A)
    where A: FrameAllocator {
        self.map( Page::containing(VAddr::from(frame.base_addr().0 as usize))
                , frame
                , flags
                , alloc )
    }

    fn map_to_any<A>( &mut self
                    , page: VirtualPage
                    , flags: EntryFlags
                    , alloc: &A)
    where A: FrameAllocator {
        let frame = unsafe {
            alloc.allocate()
             // TODO: would we rather rewrite this to return
             // a `Result`? I think so.
                 .expect("Couldn't map page, out of frames!")
        };
        self.map(page, frame, flags, alloc);
    }

    /// Unmap the given `VirtualPage`.
    ///
    /// All freed frames are returned to the given `FrameAllocator`.
    fn unmap<A>(&mut self, page: VirtualPage, alloc: &A)
    where A: FrameAllocator {
        use self::tlb::Flush;

        // get the page table entry corresponding to the page.
        let ref mut entry
            = self.pml4_mut()
                  .page_table_mut_for(page) // get the page table for the page
                  .expect("Could not unmap, huge pages not supported!")
                  [page.base()];        // index the entry from the table

        // get the pointed frame for the page table entry.
        let frame = entry.get_frame()
                         .expect("Could not unmap page that was not mapped!");

        // mark the page table entry as unused
        entry.set_unused();

        // deallocate the frame and flush the translation lookaside buffer
        // this is safe because we're in kernel mode
        assert!( page.flush()
               , "Could not flush TLB, we were not in kernel mode!");
        unsafe {
            // this is hopefully safe because nobody else should be using an
            // allocated page frame
            alloc.deallocate(frame);
        }
        // TODO: check if page tables containing the unmapped page are empty
        //       and deallocate them too?
    }

}

impl ActivePML4 {

    pub unsafe fn new() -> Self {
        ActivePML4(Unique::new(PML4_PTR))
    }

    fn pml4(&self) -> &Table<PML4Level> {
        unsafe { self.0.get() }
    }

    fn pml4_mut(&mut self) -> &mut Table<PML4Level> {
        unsafe { self.0.get_mut() }
    }

    /// Returns true if the given page is mapped.
    #[inline]
    pub fn is_mapped(&self, page: &VirtualPage) -> bool {
         self.translate_page(*page).is_some()
    }

}

/// An inactive page table that the CPU is not currently using
pub struct InactivePageTable {
    pml4_frame: PhysicalPage
}

impl InactivePageTable {
    pub fn new( frame: PhysicalPage
              , active_table: &mut ActivePageTable
              , temp: &mut TempPage)
              -> Self {
        {
            let table = temp.map_to_table(frame.clone(), active_table)
                            .zero();
            table[511].set( frame.clone(), PRESENT | WRITABLE);
        }
        unimplemented!()
    }
}

pub fn test_paging<A>(alloc: &A)
where A: FrameAllocator {
    // This testing code shamelessly stolen from Phil Oppermann.
    let mut pml4 = unsafe { ActivePML4::new() };

    // address 0 is mapped
    println!("Some = {:?}", pml4.translate(VAddr::from(0)));
     // second PT entry
    println!("Some = {:?}", pml4.translate(VAddr::from(4096)));
    // second PD entry
    println!("Some = {:?}", pml4.translate(VAddr::from(512 * 4096)));
    // 300th PD entry
    println!("Some = {:?}", pml4.translate(VAddr::from(300 * 512 * 4096)));
    // second PDPT entry
    println!("None = {:?}", pml4.translate(VAddr::from(512 * 512 * 4096)));
    // last mapped byte
    println!("Some = {:?}", pml4.translate(VAddr::from(512 * 512 * 4096 - 1)));

    let addr = VAddr::from(42 * 512 * 512 * 4096); // 42th PDPT entry
    let page = VirtualPage::containing(addr);
    let frame = unsafe { alloc.allocate().expect("no more frames") };
    println!("None = {:?}, map to {:?}",
             pml4.translate(addr),
             frame);
    pml4.map(page, frame, EntryFlags::empty(), alloc);
    println!("Some = {:?}", pml4.translate(addr));
    println!( "next free frame: {:?}"
            , unsafe { alloc.allocate() });

    //println!("{:#x}", *(Page::containing(addr).as_ptr()));

    pml4.unmap(Page::containing(addr), alloc);
    println!("None = {:?}", pml4.translate(addr));

}

/// Remaps the kernel using 4KiB pages.
pub fn kernel_remap<A>(info: &multiboot2::Info, alloc: &A) -> ActivePageTable
where A: FrameAllocator {

    // create a  temporary page for switching page tables
    // page number chosen fairly arbitrarily.
    const TEMP_PAGE_NUMBER: usize = 0xDECAF000;
    let mut temp_page = TempPage::new(TEMP_PAGE_NUMBER, alloc);

    // old and new page tables
    let mut current_table = unsafe { ActivePageTable::new() };
    let mut new_table = unsafe {
        InactivePageTable::new(
             alloc.allocate().expect("Out of physical pages!")
          , &mut current_table
          , &mut temp_page
          )
    };

    // actually remap the kernel --------------------------------------------
    current_table.using(&mut new_table, &mut temp_page, |pml4| {
        let sections // extract allocated ELF sections
            = info.elf_sections()
                  .expect("Can't remap the kernel, no elf sections tag!")
                  .sections()
                  .filter(|section| section.is_allocated());

        for section in sections { // remap ELF secctions
            assert!( section.addr().is_page_aligned()
                   , "Section address must be page aligned to remap!");
            println!( "Identity mapping section at {:?} with size {:?}"
                    , section.addr()
                    , section.length() );

            let flags = EntryFlags::from(&section);

            let start_frame = PhysicalPage::from(section.addr());
            let end_frame = PhysicalPage::from(section.end_addr());

            for frame in start_frame .. end_frame {
                pml4.identity_map(frame, flags, alloc)
            }
        }

        // remap VGA buffer
        println!( "Identity mapping VGA buffer" );
        let vga_buffer_frame = PhysicalPage::from(PAddr::from(0xb8000));
        pml4.identity_map(vga_buffer_frame, WRITABLE, alloc);

        // remap Multiboot info
        println!( "Identity mapping multiboot info" );
        let multiboot_start = PhysicalPage::from(info.start_addr());
        let multiboot_end = PhysicalPage::from(info.end_addr());

        for frame in multiboot_start .. multiboot_end {
            pml4.identity_map(frame, PRESENT, alloc)
        }

    });

    // switch page tables ---------------------------------------------------
    let old_table = current_table.replace_with(&mut new_table);
    println!("Successfully switched to remapped page table!");

    // create guard page at the location of the old PML4 table
    let old_pml4_page
        = VirtualPage::containing(
            VAddr::from(*(old_table.pml4_frame.base()) as usize)
        );
    current_table.unmap(old_pml4_page, alloc);
    println!("Unmapped guard page at {:?}", old_pml4_page.base());

    current_table
}
<commit_msg>[x86_64] finish implementing InactivePageTable::new() OOPS i am stupid<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Paging
//!
//! The `x86_64` architecture uses a four-level page table structure. The top
//! page table is called the Page Meta-Level 4 (PML4) table, followed by
//! the Page Directory Pointer Table (PDPT), Page Directory (PD) table, and
//! finally the bottom-level Page Table (PT).
use core::ops;
use core::ptr::Unique;

use multiboot2;

use arch::cpu::control_regs::cr3;

use memory::VAddr;
use memory::paging::{Page, VirtualPage, Mapper};
use memory::alloc::FrameAllocator;

use super::{PhysicalPage, PAddr, PAGE_SIZE};

use self::table::*;
use self::temp::TempPage;

pub mod table;
pub mod tlb;
pub mod temp;

pub struct ActivePageTable { pml4: ActivePML4 }

impl ops::Deref for ActivePageTable {
    type Target = ActivePML4;

    fn deref(&self) -> &ActivePML4 {
        &self.pml4
    }
}

impl ops::DerefMut for ActivePageTable {
    fn deref_mut(&mut self) -> &mut ActivePML4 {
        &mut self.pml4
    }
}

impl ActivePageTable {
    pub unsafe fn new() -> ActivePageTable {
        ActivePageTable { pml4: ActivePML4::new() }
    }

    /// Execute a closure with the recursive mapping temporarily changed to a
    /// new page table
    pub fn using<F>( &mut self
                   , table: &mut InactivePageTable
                   , temp_page: &mut temp::TempPage
                   , f: F)
    where F: FnOnce(&mut ActivePML4) {
        use self::tlb::flush_all;
        {
            // back up the current PML4 frame
            let prev_pml4_frame = unsafe {
                // this is safe to execute; we are in kernel mode
                cr3::current_pagetable_frame()
            };

            // remap the 511th PML4 entry (the recursive entry) to map to the // frame containing the new PML4.
            self.pml4_mut()[511]
                .set(table.pml4_frame, PRESENT | WRITABLE);
            unsafe {
                // this is safe to execute; we are in kernel mode
                flush_all();
            }

            // execute the closure
            f(self);

            // remap the 511th entry to point back to the original frame
            self.pml4_mut()[511]
                .set(prev_pml4_frame, PRESENT | WRITABLE);

            unsafe {
                // this is safe to execute; we are in kernel mode
                flush_all();
            }
        }
        temp_page.unmap(self);

    }

    /// Replace the current `ActivePageTable` with the given `InactivePageTable`
    ///
    /// # Arguments:
    /// + `new_table`: the `InactivePageTable` that will replace the current
    ///                `ActivePageTable`.
    ///
    /// # Returns:
    /// + the old active page table as an `InactivePageTable`.
    pub fn replace_with(&mut self, new_table: &mut InactivePageTable)
                       -> InactivePageTable {
        unsafe {
            // this is safe to execute; we are in kernel mode
            let old_pml4_frame = cr3::current_pagetable_frame();

            cr3::set_pagetable_frame(new_table.pml4_frame);

            InactivePageTable {
                pml4_frame: old_pml4_frame
            }
        }
    }

}

/// Struct representing the currently active PML4 instance.
///
/// The `ActivePML4` is a `Unique` reference to a PML4-level page table. It's
/// unique because, well, there can only be one active PML4 at a given time.
///
///
pub struct ActivePML4(Unique<Table<PML4Level>>);

/// The active PML4 table is the single point of entry for page mapping.
impl Mapper for ActivePML4 {
    type Flags = EntryFlags;

    fn translate(&self, vaddr: VAddr) -> Option<PAddr> {
        let offset = *vaddr % PAGE_SIZE as usize;
        self.translate_page(Page::containing(vaddr))
            .map(|frame| PAddr::from(frame.number + offset as u64) )
    }

    fn translate_page(&self, page: VirtualPage) -> Option<PhysicalPage> {
        let addr = page.base();
        let pdpt = self.pml4().next_table(addr);

        let huge_page = || {
            pdpt.and_then(|pdpt|
                pdpt[addr]
                    .do_huge(PDLevel::index_of(addr) + PTLevel::index_of(addr))
                    .or_else(|| {
                        pdpt.next_table(addr).and_then(|pd|
                            pd[addr].do_huge(PTLevel::index_of(addr))
                        )
                    })
                )
        };

        pdpt.and_then(|pdpt| pdpt.next_table(addr))
            .and_then(|pd| pd.next_table(addr))
            .and_then(|pt| pt[addr].get_frame())
            .or_else(huge_page)
    }

    /// Modifies the page tables so that `page` maps to `frame`.
    ///
    /// # Arguments
    /// + `page`: the virtual `Page` to map
    /// + `frame`: the physical `Frame` that `Page` should map to.
    /// + `flags`: the page table entry flags.
    /// + `alloc`: a memory allocator
    fn map<A>( &mut self, page: VirtualPage, frame: PhysicalPage
             , flags: EntryFlags, alloc: &A)
    where A: FrameAllocator {
        // base virtual address of page being mapped
        let addr = page.base();

        // access or create all the lower-level page tables.
        let mut page_table
            // get the PML4
            = self.pml4_mut()
                  // get or create the PDPT table at the page's PML4 index
                  .create_next(addr, alloc)
                  // get or create the PD table at the page's PDPT index
                  .create_next(addr, alloc)
                  // get or create the page table at the  page's PD table index
                  .create_next(addr, alloc);

        // check if the page at that index is not currently in use, as we
        // cannot map a page which is currently in use.
        assert!(page_table[addr].is_unused()
               , "Could not map frame {:?}, page table entry {} is already \
                  in use!", frame, PTLevel::index_of(addr));
        // set the page table entry at that index
        page_table[addr].set(frame, flags | table::PRESENT);
    }

    fn identity_map<A>(&mut self, frame: PhysicalPage, flags: EntryFlags
                      , alloc: &A)
    where A: FrameAllocator {
        self.map( Page::containing(VAddr::from(frame.base_addr().0 as usize))
                , frame
                , flags
                , alloc )
    }

    fn map_to_any<A>( &mut self
                    , page: VirtualPage
                    , flags: EntryFlags
                    , alloc: &A)
    where A: FrameAllocator {
        let frame = unsafe {
            alloc.allocate()
             // TODO: would we rather rewrite this to return
             // a `Result`? I think so.
                 .expect("Couldn't map page, out of frames!")
        };
        self.map(page, frame, flags, alloc);
    }

    /// Unmap the given `VirtualPage`.
    ///
    /// All freed frames are returned to the given `FrameAllocator`.
    fn unmap<A>(&mut self, page: VirtualPage, alloc: &A)
    where A: FrameAllocator {
        use self::tlb::Flush;

        // get the page table entry corresponding to the page.
        let ref mut entry
            = self.pml4_mut()
                  .page_table_mut_for(page) // get the page table for the page
                  .expect("Could not unmap, huge pages not supported!")
                  [page.base()];        // index the entry from the table

        // get the pointed frame for the page table entry.
        let frame = entry.get_frame()
                         .expect("Could not unmap page that was not mapped!");

        // mark the page table entry as unused
        entry.set_unused();

        // deallocate the frame and flush the translation lookaside buffer
        // this is safe because we're in kernel mode
        assert!( page.flush()
               , "Could not flush TLB, we were not in kernel mode!");
        unsafe {
            // this is hopefully safe because nobody else should be using an
            // allocated page frame
            alloc.deallocate(frame);
        }
        // TODO: check if page tables containing the unmapped page are empty
        //       and deallocate them too?
    }

}

impl ActivePML4 {

    pub unsafe fn new() -> Self {
        ActivePML4(Unique::new(PML4_PTR))
    }

    fn pml4(&self) -> &Table<PML4Level> {
        unsafe { self.0.get() }
    }

    fn pml4_mut(&mut self) -> &mut Table<PML4Level> {
        unsafe { self.0.get_mut() }
    }

    /// Returns true if the given page is mapped.
    #[inline]
    pub fn is_mapped(&self, page: &VirtualPage) -> bool {
         self.translate_page(*page).is_some()
    }

}

/// An inactive page table that the CPU is not currently using
pub struct InactivePageTable {
    pml4_frame: PhysicalPage
}

impl InactivePageTable {
    pub fn new( frame: PhysicalPage
              , active_table: &mut ActivePageTable
              , temp: &mut TempPage)
              -> Self {
        {
            let table = temp.map_to_table(frame.clone(), active_table)
                            .zero();
            table[511].set( frame.clone(), PRESENT | WRITABLE);
        }
        temp.unmap(active_table);

        InactivePageTable { pml4_frame: frame }
    }
}

pub fn test_paging<A>(alloc: &A)
where A: FrameAllocator {
    // This testing code shamelessly stolen from Phil Oppermann.
    let mut pml4 = unsafe { ActivePML4::new() };

    // address 0 is mapped
    println!("Some = {:?}", pml4.translate(VAddr::from(0)));
     // second PT entry
    println!("Some = {:?}", pml4.translate(VAddr::from(4096)));
    // second PD entry
    println!("Some = {:?}", pml4.translate(VAddr::from(512 * 4096)));
    // 300th PD entry
    println!("Some = {:?}", pml4.translate(VAddr::from(300 * 512 * 4096)));
    // second PDPT entry
    println!("None = {:?}", pml4.translate(VAddr::from(512 * 512 * 4096)));
    // last mapped byte
    println!("Some = {:?}", pml4.translate(VAddr::from(512 * 512 * 4096 - 1)));

    let addr = VAddr::from(42 * 512 * 512 * 4096); // 42th PDPT entry
    let page = VirtualPage::containing(addr);
    let frame = unsafe { alloc.allocate().expect("no more frames") };
    println!("None = {:?}, map to {:?}",
             pml4.translate(addr),
             frame);
    pml4.map(page, frame, EntryFlags::empty(), alloc);
    println!("Some = {:?}", pml4.translate(addr));
    println!( "next free frame: {:?}"
            , unsafe { alloc.allocate() });

    //println!("{:#x}", *(Page::containing(addr).as_ptr()));

    pml4.unmap(Page::containing(addr), alloc);
    println!("None = {:?}", pml4.translate(addr));

}

/// Remaps the kernel using 4KiB pages.
pub fn kernel_remap<A>(info: &multiboot2::Info, alloc: &A) -> ActivePageTable
where A: FrameAllocator {

    // create a  temporary page for switching page tables
    // page number chosen fairly arbitrarily.
    const TEMP_PAGE_NUMBER: usize = 0xDECAF000;
    let mut temp_page = TempPage::new(TEMP_PAGE_NUMBER, alloc);
    println!(" . . Created temporary page.");

    // old and new page tables
    let mut current_table = unsafe { ActivePageTable::new() };
    println!(" . . Got current page table.");

    let mut new_table = unsafe {
        InactivePageTable::new(
             alloc.allocate().expect("Out of physical pages!")
          , &mut current_table
          , &mut temp_page
          )
    };
    println!(" . . Created new page table.");

    // actually remap the kernel --------------------------------------------
    current_table.using(&mut new_table, &mut temp_page, |pml4| {
        let sections // extract allocated ELF sections
            = info.elf_sections()
                  .expect("Can't remap the kernel, no elf sections tag!")
                  .sections()
                  .filter(|section| section.is_allocated());

        for section in sections { // remap ELF secctions
            assert!( section.addr().is_page_aligned()
                   , "Section address must be page aligned to remap!");
            println!( " . . Identity mapping section at {:?} with size {:?}"
                    , section.addr()
                    , section.length() );

            let flags = EntryFlags::from(&section);

            let start_frame = PhysicalPage::from(section.addr());
            let end_frame = PhysicalPage::from(section.end_addr());

            for frame in start_frame .. end_frame {
                pml4.identity_map(frame, flags, alloc)
            }
        }

        // remap VGA buffer
        println!( " . . Identity mapping VGA buffer" );
        let vga_buffer_frame = PhysicalPage::from(PAddr::from(0xb8000));
        pml4.identity_map(vga_buffer_frame, WRITABLE, alloc);

        // remap Multiboot info
        println!( " . . Identity mapping multiboot info" );
        let multiboot_start = PhysicalPage::from(info.start_addr());
        let multiboot_end = PhysicalPage::from(info.end_addr());

        for frame in multiboot_start .. multiboot_end {
            pml4.identity_map(frame, PRESENT, alloc)
        }

    });

    // switch page tables ---------------------------------------------------
    let old_table = current_table.replace_with(&mut new_table);
    println!(" . . Successfully switched to remapped page table!");

    // create guard page at the location of the old PML4 table
    let old_pml4_page
        = VirtualPage::containing(
            VAddr::from(*(old_table.pml4_frame.base()) as usize)
        );
    current_table.unmap(old_pml4_page, alloc);
    println!(" . . Unmapped guard page at {:?}", old_pml4_page.base());

    current_table
}
<|endoftext|>